mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-07 00:15:48 -04:00
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.
This commit is contained in:
parent
3d372e615c
commit
c22f5de690
@ -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,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
|
||||
|
@ -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,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 ) );
|
||||
|
@ -20,10 +20,9 @@
|
||||
|
||||
#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
|
||||
@ -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<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;
|
||||
|
||||
|
@ -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 <QPainter>
|
||||
|
||||
|
||||
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
BIN
tests/testdata/control_images/annotations/expected_html_feature/expected_html_feature.png
vendored
Normal file
BIN
tests/testdata/control_images/annotations/expected_html_feature/expected_html_feature.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
BIN
tests/testdata/control_images/annotations/expected_html_nofeature/expected_html_nofeature.png
vendored
Normal file
BIN
tests/testdata/control_images/annotations/expected_html_nofeature/expected_html_nofeature.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
41
tests/testdata/test_html_feature.html
vendored
Normal file
41
tests/testdata/test_html_feature.html
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body
|
||||
{
|
||||
background-color: transparent;
|
||||
margin: 6px;
|
||||
padding: 0px;
|
||||
}
|
||||
table
|
||||
{
|
||||
width: 300px;
|
||||
border-spacing: 6px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
table tr
|
||||
{
|
||||
height: 40px;
|
||||
}
|
||||
table td
|
||||
{
|
||||
border: 2px solid black;
|
||||
font-family: arial;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
tr.row1
|
||||
{
|
||||
background-color: yellow;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr class="row1"><td>[% "station" %]</td><td>[% "suburb" %]</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user