From 0cb985465b839ae4a4375bcbf4958c92f704b653 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 19 Oct 2014 11:02:16 +1100 Subject: [PATCH] [composer] Fix area calculation in expressions with OTF (fix #9791) Adds an argument to QgsExpression::replaceExpressionText to allow passing an optional QgsDistanceArea to use during calculations. Ensure that html and label composer items correctly specify this argument. --- python/core/qgsexpression.sip | 13 +++++++-- src/core/composer/qgscomposerhtml.cpp | 25 +++++++++++++++- src/core/composer/qgscomposerhtml.h | 3 ++ src/core/composer/qgscomposerlabel.cpp | 40 ++++++++++++++++++++++---- src/core/composer/qgscomposerlabel.h | 4 +-- src/core/qgsexpression.cpp | 8 +++++- src/core/qgsexpression.h | 10 ++++++- 7 files changed, 90 insertions(+), 13 deletions(-) diff --git a/python/core/qgsexpression.sip b/python/core/qgsexpression.sip index 248e2514d7d..e977afeeb2f 100644 --- a/python/core/qgsexpression.sip +++ b/python/core/qgsexpression.sip @@ -90,17 +90,26 @@ class QgsExpression //! Sets the geometry calculator used in evaluation of expressions, // instead of the default. void setGeomCalculator( const QgsDistanceArea &calc ); - + /** This function currently replaces each expression between [% and %] in the string with the result of its evaluation on the feature passed as argument. Additional substitutions can be passed through the substitutionMap parameter + @param action + @param feat + @param layer + @param substitutionMap + @param distanceArea optional QgsDistanceArea. If specified, the QgsDistanceArea is used for distance + and area conversion */ static QString replaceExpressionText( const QString &action, const QgsFeature *feat, QgsVectorLayer *layer, - const QMap *substitutionMap = 0 ); + const QMap *substitutionMap = 0, + const QgsDistanceArea* distanceArea = 0 + ); + enum UnaryOperator { uoNot, diff --git a/src/core/composer/qgscomposerhtml.cpp b/src/core/composer/qgscomposerhtml.cpp index f4ee54a97c2..12b538d55ce 100644 --- a/src/core/composer/qgscomposerhtml.cpp +++ b/src/core/composer/qgscomposerhtml.cpp @@ -22,6 +22,8 @@ #include "qgsexpression.h" #include "qgslogger.h" #include "qgsnetworkcontentfetcher.h" +#include "qgsvectorlayer.h" +#include "qgsproject.h" #include #include @@ -42,9 +44,11 @@ QgsComposerHtml::QgsComposerHtml( QgsComposition* c, bool createUndoCommands ) , mMaxBreakDistance( 10 ) , mExpressionFeature( 0 ) , mExpressionLayer( 0 ) + , mDistanceArea( 0 ) , mEnableUserStylesheet( false ) , mFetcher( 0 ) { + mDistanceArea = new QgsDistanceArea(); mHtmlUnitsToMM = htmlUnitsToMM(); mWebPage = new QWebPage(); mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff ); @@ -92,14 +96,17 @@ QgsComposerHtml::QgsComposerHtml() , mMaxBreakDistance( 10 ) , mExpressionFeature( 0 ) , mExpressionLayer( 0 ) + , mDistanceArea( 0 ) , mFetcher( 0 ) { + mDistanceArea = new QgsDistanceArea(); mFetcher = new QgsNetworkContentFetcher(); connect( mFetcher, SIGNAL( finished() ), this, SLOT( frameLoaded() ) ); } QgsComposerHtml::~QgsComposerHtml() { + delete mDistanceArea; delete mWebPage; delete mRenderedPage; mFetcher->deleteLater(); @@ -179,7 +186,7 @@ void QgsComposerHtml::loadHtml() //evaluate expressions if ( mEvaluateExpressions ) { - loadedHtml = QgsExpression::replaceExpressionText( loadedHtml, mExpressionFeature, mExpressionLayer ); + loadedHtml = QgsExpression::replaceExpressionText( loadedHtml, mExpressionFeature, mExpressionLayer, 0, mDistanceArea ); } mLoaded = false; @@ -522,6 +529,22 @@ void QgsComposerHtml::setExpressionContext( QgsFeature* feature, QgsVectorLayer* { mExpressionFeature = feature; mExpressionLayer = layer; + + //setup distance area conversion + if ( layer ) + { + mDistanceArea->setSourceCrs( layer->crs().srsid() ); + } + else if ( mComposition ) + { + //set to composition's mapsettings' crs + mDistanceArea->setSourceCrs( mComposition->mapSettings().destinationCrs().srsid() ); + } + if ( mComposition ) + { + mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() ); + } + mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); } void QgsComposerHtml::refreshExpressionContext() diff --git a/src/core/composer/qgscomposerhtml.h b/src/core/composer/qgscomposerhtml.h index 8add3719b86..edd850fdaee 100644 --- a/src/core/composer/qgscomposerhtml.h +++ b/src/core/composer/qgscomposerhtml.h @@ -24,6 +24,7 @@ class QImage; class QgsFeature; class QgsVectorLayer; class QgsNetworkContentFetcher; +class QgsDistanceArea; class CORE_EXPORT QgsComposerHtml: public QgsComposerMultiFrame { @@ -242,6 +243,8 @@ class CORE_EXPORT QgsComposerHtml: public QgsComposerMultiFrame QgsFeature* mExpressionFeature; QgsVectorLayer* mExpressionLayer; + QgsDistanceArea* mDistanceArea; + QString mUserStylesheet; bool mEnableUserStylesheet; diff --git a/src/core/composer/qgscomposerlabel.cpp b/src/core/composer/qgscomposerlabel.cpp index b70d3444321..ad60aff3b9c 100755 --- a/src/core/composer/qgscomposerlabel.cpp +++ b/src/core/composer/qgscomposerlabel.cpp @@ -21,6 +21,8 @@ #include "qgsexpression.h" #include "qgsnetworkaccessmanager.h" #include "qgscomposermodel.h" +#include "qgsvectorlayer.h" +#include "qgsproject.h" #include #include @@ -32,12 +34,20 @@ #include #include -QgsComposerLabel::QgsComposerLabel( QgsComposition *composition ): - QgsComposerItem( composition ), mHtmlState( 0 ), mHtmlUnitsToMM( 1.0 ), - mHtmlLoaded( false ), mMargin( 1.0 ), mFontColor( QColor( 0, 0, 0 ) ), - mHAlignment( Qt::AlignLeft ), mVAlignment( Qt::AlignTop ), - mExpressionFeature( 0 ), mExpressionLayer( 0 ) +QgsComposerLabel::QgsComposerLabel( QgsComposition *composition ) + : QgsComposerItem( composition ) + , mHtmlState( 0 ) + , mHtmlUnitsToMM( 1.0 ) + , mHtmlLoaded( false ) + , mMargin( 1.0 ) + , mFontColor( QColor( 0, 0, 0 ) ) + , mHAlignment( Qt::AlignLeft ) + , mVAlignment( Qt::AlignTop ) + , mExpressionFeature( 0 ) + , mExpressionLayer( 0 ) + , mDistanceArea( 0 ) { + mDistanceArea = new QgsDistanceArea(); mHtmlUnitsToMM = htmlUnitsToMM(); //get default composer font from settings @@ -71,6 +81,7 @@ QgsComposerLabel::QgsComposerLabel( QgsComposition *composition ): QgsComposerLabel::~QgsComposerLabel() { + delete mDistanceArea; } void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget ) @@ -221,6 +232,23 @@ void QgsComposerLabel::setExpressionContext( QgsFeature* feature, QgsVectorLayer mExpressionFeature = feature; mExpressionLayer = layer; mSubstitutions = substitutions; + + //setup distance area conversion + if ( layer ) + { + mDistanceArea->setSourceCrs( layer->crs().srsid() ); + } + else if ( mComposition ) + { + //set to composition's mapsettings' crs + mDistanceArea->setSourceCrs( mComposition->mapSettings().destinationCrs().srsid() ); + } + if ( mComposition ) + { + mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() ); + } + mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); + // Force label to redraw -- fixes label printing for labels with blend modes when used with atlas update(); } @@ -248,7 +276,7 @@ QString QgsComposerLabel::displayText() const replaceDateText( displayText ); QMap subs = mSubstitutions; subs[ "$page" ] = QVariant(( int )mComposition->itemPageNumber( this ) + 1 ); - return QgsExpression::replaceExpressionText( displayText, mExpressionFeature, mExpressionLayer, &subs ); + return QgsExpression::replaceExpressionText( displayText, mExpressionFeature, mExpressionLayer, &subs, mDistanceArea ); } void QgsComposerLabel::replaceDateText( QString& text ) const diff --git a/src/core/composer/qgscomposerlabel.h b/src/core/composer/qgscomposerlabel.h index b6c98737b80..1438274bd92 100644 --- a/src/core/composer/qgscomposerlabel.h +++ b/src/core/composer/qgscomposerlabel.h @@ -22,6 +22,7 @@ class QgsVectorLayer; class QgsFeature; +class QgsDistanceArea; /** \ingroup MapComposer * A label that can be placed onto a map composition. @@ -143,8 +144,7 @@ class CORE_EXPORT QgsComposerLabel: public QgsComposerItem QgsFeature* mExpressionFeature; QgsVectorLayer* mExpressionLayer; QMap mSubstitutions; - - + QgsDistanceArea* mDistanceArea; }; #endif diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index cb1f5e163b1..60da86dd2f0 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -1989,7 +1989,7 @@ void QgsExpression::acceptVisitor( QgsExpression::Visitor& v ) const QString QgsExpression::replaceExpressionText( const QString &action, const QgsFeature *feat, QgsVectorLayer *layer, - const QMap *substitutionMap ) + const QMap *substitutionMap , const QgsDistanceArea *distanceArea ) { QString expr_action; @@ -2030,6 +2030,12 @@ QString QgsExpression::replaceExpressionText( const QString &action, const QgsFe continue; } + if ( distanceArea ) + { + //if QgsDistanceArea specified for area/distance conversion, use it + exp.setGeomCalculator( *distanceArea ); + } + QVariant result; if ( layer ) { diff --git a/src/core/qgsexpression.h b/src/core/qgsexpression.h index 8ed3e61dbf3..7bde0c16dd3 100644 --- a/src/core/qgsexpression.h +++ b/src/core/qgsexpression.h @@ -183,10 +183,18 @@ class CORE_EXPORT QgsExpression Additional substitutions can be passed through the substitutionMap parameter + @param action + @param feat + @param layer + @param substitutionMap + @param distanceArea optional QgsDistanceArea. If specified, the QgsDistanceArea is used for distance + and area conversion */ static QString replaceExpressionText( const QString &action, const QgsFeature *feat, QgsVectorLayer *layer, - const QMap *substitutionMap = 0 ); + const QMap *substitutionMap = 0, + const QgsDistanceArea* distanceArea = 0 + ); enum UnaryOperator { uoNot,