Skip to content

Commit

Permalink
Merge pull request #4825
Browse files Browse the repository at this point in the history
Various new expression functions and possibilities
  • Loading branch information
m-kuhn committed Jul 8, 2017
2 parents 6b6a52b + 89a06f6 commit ada9ce4
Show file tree
Hide file tree
Showing 26 changed files with 402 additions and 75 deletions.
5 changes: 5 additions & 0 deletions python/core/expression/qgsexpressionfunction.sip
Expand Up @@ -249,6 +249,11 @@ The help text for the function.
:rtype: QVariant
%End

virtual QVariant run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent );
%Docstring
:rtype: QVariant
%End

bool operator==( const QgsExpressionFunction &other ) const;

virtual bool handlesNull() const;
Expand Down
2 changes: 1 addition & 1 deletion python/core/geometry/qgscompoundcurve.sip
Expand Up @@ -77,7 +77,7 @@ class QgsCompoundCurve: QgsCurve

void addCurve( QgsCurve *c /Transfer/ );
%Docstring
Adds a curve to the geometr (takes ownership)
Adds a curve to the geometry (takes ownership)
%End

void removeCurve( int i );
Expand Down
9 changes: 9 additions & 0 deletions python/core/qgsexpressioncontext.sip
Expand Up @@ -809,6 +809,15 @@ class QgsExpressionContextUtils
:rtype: QgsExpressionContextScope
%End

static QgsExpressionContextScope *mapToolCaptureScope( const QList<QgsPointLocator::Match> &matches ) /Factory/;
%Docstring
Sets the expression context variables which are available for expressions triggered by
a map tool capture like add feature.

.. versionadded:: 3.0
:rtype: QgsExpressionContextScope
%End

static QgsExpressionContextScope *updateSymbolScope( const QgsSymbol *symbol, QgsExpressionContextScope *symbolScope = 0 );
%Docstring
Updates a symbol scope related to a QgsSymbol to an expression context.
Expand Down
8 changes: 8 additions & 0 deletions python/gui/qgsmaptoolcapture.sip
Expand Up @@ -42,6 +42,14 @@ Adds a whole curve (e.g. circularstring) to the captured geometry. Curve must be
:rtype: QgsCompoundCurve
%End

QList<QgsPointLocator::Match> snappingMatches() const;
%Docstring
Return a list of matches for each point on the captureCurve.

.. versionadded:: 3.0
:rtype: list of QgsPointLocator.Match
%End

virtual void cadCanvasMoveEvent( QgsMapMouseEvent *e );

virtual void keyPressEvent( QKeyEvent *e );
Expand Down
7 changes: 7 additions & 0 deletions resources/function_help/json/array_first
@@ -0,0 +1,7 @@
{
"name": "array_first",
"type": "function",
"description": "Returns the first value of an array.",
"arguments": [ {"arg":"array","description":"an array"} ],
"examples": [ { "expression":"array_first(array('a','b','c'))", "returns":"'a'"}]
}
7 changes: 7 additions & 0 deletions resources/function_help/json/array_last
@@ -0,0 +1,7 @@
{
"name": "array_last",
"type": "function",
"description": "Returns the last value of an array.",
"arguments": [ {"arg":"array","description":"an array"} ],
"examples": [ { "expression":"array_last(array('a','b','c'))", "returns":"'c'"}]
}
8 changes: 8 additions & 0 deletions resources/function_help/json/get_feature_by_id
@@ -0,0 +1,8 @@
{
"name": "get_feature_by_id",
"type": "function",
"description": "Returns the feature with an id on a layer.",
"arguments": [ {"arg":"layer","description":"layer, layer name or layer id"},
{"arg":"feature_id","description":"the id of the feature which should be returned"}],
"examples": [ { "expression":"get_feature('streets', 1)", "returns":"the feature with the id 1 on the layer \"streets\""}]
}
11 changes: 11 additions & 0 deletions resources/function_help/json/with_variable
@@ -0,0 +1,11 @@
{
"name": "with_variable",
"type": "function",
"description": "This function sets a variable for any expression code that will be provided as 3rd argument. This is only useful for complicated expressions, where the same calculated value needs to be used in different places.",
"arguments": [
{"arg":"name","description":"the name of the variable to set"},
{"arg":"value","description":"the value to set"},
{"arg":"node","description":"the expression for which the variable will be available"}
],
"examples": [ { "expression":"with_variable('my_sum', 1 + 2 + 3, @my_sum * 2 + @my_sum * 5)", "returns":"42"}]
}
14 changes: 14 additions & 0 deletions src/app/qgsattributetypedialog.cpp
Expand Up @@ -71,6 +71,8 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx
isFieldEditableCheckBox->setEnabled( false );
}

mExpressionWidget->registerExpressionContextGenerator( this );

connect( mExpressionWidget, &QgsExpressionLineEdit::expressionChanged, this, &QgsAttributeTypeDialog::defaultExpressionChanged );
connect( mUniqueCheckBox, &QCheckBox::toggled, this, [ = ]( bool checked )
{
Expand Down Expand Up @@ -293,6 +295,18 @@ void QgsAttributeTypeDialog::setDefaultValueExpression( const QString &expressio
mExpressionWidget->setExpression( expression );
}

QgsExpressionContext QgsAttributeTypeDialog::createExpressionContext() const
{
QgsExpressionContext context;
context
<< QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() )
<< QgsExpressionContextUtils::layerScope( mLayer )
<< QgsExpressionContextUtils::mapToolCaptureScope( QList<QgsPointLocator::Match>() );

return context;
}

QString QgsAttributeTypeDialog::constraintExpression() const
{
return constraintExpressionWidget->asExpression();
Expand Down
4 changes: 3 additions & 1 deletion src/app/qgsattributetypedialog.h
Expand Up @@ -26,7 +26,7 @@

class QDialog;

class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttributeTypeDialog
class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttributeTypeDialog, QgsExpressionContextGenerator
{
Q_OBJECT

Expand Down Expand Up @@ -163,6 +163,8 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
*/
void setDefaultValueExpression( const QString &expression );

QgsExpressionContext createExpressionContext() const override;

private slots:

/**
Expand Down
5 changes: 4 additions & 1 deletion src/app/qgsfeatureaction.cpp
Expand Up @@ -161,7 +161,7 @@ bool QgsFeatureAction::editFeature( bool showModal )
return true;
}

bool QgsFeatureAction::addFeature( const QgsAttributeMap &defaultAttributes, bool showModal )
bool QgsFeatureAction::addFeature( const QgsAttributeMap &defaultAttributes, bool showModal, QgsExpressionContextScope *scope SIP_TRANSFER )
{
if ( !mLayer || !mLayer->isEditable() )
return false;
Expand All @@ -188,6 +188,9 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap &defaultAttributes, boo
// create new feature template - this will initialize the attributes to valid values, handling default
// values and field constraints
QgsExpressionContext context = mLayer->createExpressionContext();
if ( scope )
context.appendScope( scope );

QgsFeature newFeature = QgsVectorLayerUtils::createFeature( mLayer, mFeature->geometry(), initialAttributeValues,
&context );
*mFeature = newFeature;
Expand Down
3 changes: 2 additions & 1 deletion src/app/qgsfeatureaction.h
Expand Up @@ -29,6 +29,7 @@ class QgsIdentifyResultsDialog;
class QgsVectorLayer;
class QgsHighlight;
class QgsAttributeDialog;
class QgsExpressionContextScope;

class APP_EXPORT QgsFeatureAction : public QAction
{
Expand All @@ -51,7 +52,7 @@ class APP_EXPORT QgsFeatureAction : public QAction
*
* \returns true if feature was added if showModal is true. If showModal is false, returns true in every case
*/
bool addFeature( const QgsAttributeMap &defaultAttributes = QgsAttributeMap(), bool showModal = true );
bool addFeature( const QgsAttributeMap &defaultAttributes = QgsAttributeMap(), bool showModal = true, QgsExpressionContextScope *scope = nullptr );

private slots:
void onFeatureSaved( const QgsFeature &feature );
Expand Down
15 changes: 8 additions & 7 deletions src/app/qgsmaptooladdfeature.cpp
Expand Up @@ -48,8 +48,9 @@ QgsMapToolAddFeature::~QgsMapToolAddFeature()

bool QgsMapToolAddFeature::addFeature( QgsVectorLayer *vlayer, QgsFeature *f, bool showModal )
{
QgsExpressionContextScope *scope = QgsExpressionContextUtils::mapToolCaptureScope( snappingMatches() );
QgsFeatureAction *action = new QgsFeatureAction( tr( "add feature" ), *f, vlayer, QString(), -1, this );
bool res = action->addFeature( QgsAttributeMap(), showModal );
bool res = action->addFeature( QgsAttributeMap(), showModal, scope );
if ( showModal )
delete action;
return res;
Expand Down Expand Up @@ -250,6 +251,7 @@ void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;

QList<QgsPointLocator::Match> snappingMatchesList;
QgsCurve *curveToAdd = nullptr;
if ( hasCurvedSegments && providerSupportsCurvedSegments )
{
Expand All @@ -258,13 +260,13 @@ void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
else
{
curveToAdd = captureCurve()->curveToLine();
snappingMatchesList = snappingMatches();
}

if ( mode() == CaptureLine )
{
QgsGeometry *g = new QgsGeometry( curveToAdd );
f->setGeometry( *g );
delete g;
QgsGeometry g( curveToAdd );
f->setGeometry( g );
}
else
{
Expand All @@ -278,9 +280,8 @@ void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
poly = new QgsPolygonV2();
}
poly->setExteriorRing( curveToAdd );
QgsGeometry *g = new QgsGeometry( poly );
f->setGeometry( *g );
delete g;
QgsGeometry g( poly );
f->setGeometry( g );

QgsGeometry featGeom = f->geometry();
int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
Expand Down
1 change: 0 additions & 1 deletion src/app/qgsmaptooladdfeature.h
Expand Up @@ -44,7 +44,6 @@ class APP_EXPORT QgsMapToolAddFeature : public QgsMapToolCapture
void setCheckGeometryType( bool checkGeometryType );

private:
QVariant snappingMatchesAsVariable() const;

/** Check if CaptureMode matches layer type. Default is true.
* \since QGIS 2.12 */
Expand Down
13 changes: 13 additions & 0 deletions src/core/expression/qgsexpression.cpp
Expand Up @@ -673,6 +673,19 @@ void QgsExpression::initVariableHelp()
sVariableHelpTexts.insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) );
sVariableHelpTexts.insert( QStringLiteral( "grid_axis" ), QCoreApplication::translate( "variable_help", "Current grid annotation axis (e.g., 'x' for longitude, 'y' for latitude)." ) );

// map tool capture variables
sVariableHelpTexts.insert( QStringLiteral( "snapping_results" ), QCoreApplication::translate( "variable_help",
"<p>An array with an item for each snapped point.</p>"
"<p>Each item is a map with the following keys:</p>"
"<dl>"
"<dt>valid</dt><dd>Boolean that indicates if the snapping result is valid</dd>"
"<dt>layer</dt><dd>The layer on which the snapped feature is</dd>"
"<dt>feature_id</dt><dd>The feature id of the snapped feature</dd>"
"<dt>vertex_index</dt><dd>The index of the snapped vertex</dd>"
"<dt>distance</dt><dd>The distance between the mouse cursor and the snapped point at the time of snapping</dd>"
"</dl>" ) );


//symbol variables
sVariableHelpTexts.insert( QStringLiteral( "geometry_part_count" ), QCoreApplication::translate( "variable_help", "Number of parts in rendered feature's geometry." ) );
sVariableHelpTexts.insert( QStringLiteral( "geometry_part_num" ), QCoreApplication::translate( "variable_help", "Current geometry part number for feature being rendered." ) );
Expand Down

0 comments on commit ada9ce4

Please sign in to comment.