Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ref functions #62

Open
wants to merge 12 commits into
base: refFunctions
Choose a base branch
from
113 changes: 66 additions & 47 deletions src/core/expression/qgsexpressionfunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4886,7 +4886,7 @@ static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionCont

typedef std::function < QVariant( QgsExpression &subExp, QgsExpressionContext &subContext, const QgsSpatialIndex &spatialIndex, std::shared_ptr<QgsVectorLayer> cachedTarget, const QgsGeometry &geometry, bool testOnly, bool invert, int neighbors, double max_distance, double bboxGrow ) > overlayFunc;

static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, bool testOnly, const overlayFunc &overlayFunction, bool invert = false, double bboxGrow = 0 )
static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, bool testOnly, const overlayFunc &overlayFunction, bool invert = false, double bboxGrow = 0 )
{
// First parameter is the overlay layer
QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
Expand All @@ -4901,8 +4901,9 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
ENSURE_NO_EVAL_ERROR
subExpString = node->dump();
if ( subExpString == "NULL" ) {
testOnly = true;
if ( subExpString == "NULL" )
{
testOnly = true;
}

QgsSpatialIndex spatialIndex;
Expand All @@ -4920,10 +4921,13 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
ENSURE_NO_EVAL_ERROR
filterString = node->dump();
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the filter the comment below (see limit) does not apply. We don't want it to be evaluated with the parent context but set it literally for the request, hence the dump().

if ( filterString != "NULL" ) request.setFilterExpression (filterString); //filter cached features
if ( filterString != "NULL" )
{
request.setFilterExpression( filterString ); //filter cached features
}

int limit = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
Copy link

@enricofer enricofer Jul 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the limit attribute but when I try to read the third parameter as int I get this exception: Eval Error: Cannot convert '' to int
The same error I got reading nearest (neighbors and max_distance) optional parameters.

if (limit > 0) request.setLimit (limit);
request.setLimit( limit );

int neighbors = 1;
/*
Expand Down Expand Up @@ -4989,7 +4993,10 @@ template <t_relationFunction T>
static QVariant indexedFilteredOverlay( QgsExpression &subExp, QgsExpressionContext &subContext, const QgsSpatialIndex &spatialIndex, std::shared_ptr<QgsVectorLayer> cachedTarget, const QgsGeometry &geometry, bool testOnly, bool invert, int neighbors, double max_distance, double bboxGrow = 0 )
{
QgsRectangle intDomain = geometry.boundingBox();
if ( bboxGrow != 0 ) intDomain.grow(bboxGrow); //optional parameter to enlarge boundary context for touches and equals methods
if ( bboxGrow != 0 )
{
intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
}

const QList<QgsFeatureId> targetFeatureIds = spatialIndex.intersects( intDomain );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. What about allow layer self checking? Giving the same layer as target layer would be useful to extract cross data between features of the same layer. but to do this I should exclude current feature id. it would be simple if feature could be passed as parameter, but I see that you pass only geometry.... Any way current feature exclusion should be performed only when source layer and target layer is the same. But I can't understand where retrieve current source layer...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting idea.
You might be successful using

const QVariant layerVar = context->variable( QStringLiteral( "layer" ) );
QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( layerVar, parent );


Expand All @@ -5009,46 +5016,58 @@ static QVariant indexedFilteredOverlay( QgsExpression &subExp, QgsExpressionCont
break;

// We want a list of attributes / geometries / other expression values, evaluate now
if (!invert){
subContext.setFeature( feat );
results.append( subExp.evaluate( &subContext ) );
} else {
results.append(id);
if ( !invert )
{
subContext.setFeature( feat );
results.append( subExp.evaluate( &subContext ) );
}
else
{
results.append( id );
}
}
}
if ( testOnly ){
if ( invert ) found = !found;//for disjoint condition
return found;
} else {
if ( !invert ) return results;
else { // for disjoint condition returns the results for cached layers not intersected feats
QVariantList disjoint_results;
QgsFeature feat;
QgsFeatureIterator fi = cachedTarget->getFeatures();
while ( fi.nextFeature( feat ) ) {
if ( !results.contains(feat.id()) ) {
subContext.setFeature( feat );
disjoint_results.append( subExp.evaluate( &subContext ) );
}
if ( testOnly )
{
if ( invert )
found = !found;//for disjoint condition
return found;
}
else
{
if ( !invert )
return results;
else
{
// for disjoint condition returns the results for cached layers not intersected feats
QVariantList disjoint_results;
QgsFeature feat;
QgsFeatureIterator fi = cachedTarget->getFeatures();
while ( fi.nextFeature( feat ) )
{
if ( !results.contains( feat.id() ) )
{
subContext.setFeature( feat );
disjoint_results.append( subExp.evaluate( &subContext ) );
}
return disjoint_results;
}
return disjoint_results;
}
}
}

static QVariantList indexedFilteredNearest( QgsExpression &subExp, QgsExpressionContext &subContext, const QgsSpatialIndex &spatialIndex, std::shared_ptr<QgsVectorLayer> cachedTarget, const QgsGeometry &geometry, bool testOnly, bool invert, int neighbors, double max_distance, double bboxGrow = 0)
static QVariantList indexedFilteredNearest( QgsExpression &subExp, QgsExpressionContext &subContext, const QgsSpatialIndex &spatialIndex, std::shared_ptr<QgsVectorLayer> cachedTarget, const QgsGeometry &geometry, bool testOnly, bool invert, int neighbors, double max_distance, double bboxGrow = 0 )
{

const QList<QgsFeatureId> targetFeatureIds = spatialIndex.nearestNeighbor( geometry, neighbors, max_distance );
QVariantList results;
for ( QgsFeatureId id : targetFeatureIds )
{
QgsFeature feat = cachedTarget->getFeature( id );
subContext.setFeature( feat );
results.append( subExp.evaluate( &subContext ) );
}
return results;
const QList<QgsFeatureId> targetFeatureIds = spatialIndex.nearestNeighbor( geometry, neighbors, max_distance );
QVariantList results;
for ( QgsFeatureId id : targetFeatureIds )
{
QgsFeature feat = cachedTarget->getFeature( id );
subContext.setFeature( feat );
results.append( subExp.evaluate( &subContext ) );
}
return results;
}

static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
Expand Down Expand Up @@ -5083,7 +5102,7 @@ static QVariant fcnTestGeomOverlayCrosses( const QVariantList &values, const Qgs

static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
return executeGeomOverlay( values, context, parent, false, indexedFilteredOverlay<&QgsGeometry::equals>, false, 0.01 ); //grow amount should adapt to current units
return executeGeomOverlay( values, context, parent, false, indexedFilteredOverlay<&QgsGeometry::equals>, false, 0.01 ); //grow amount should adapt to current units
}

static QVariant fcnTestGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
Expand Down Expand Up @@ -5123,7 +5142,7 @@ static QVariant fcnTestGeomOverlayDisjoint( const QVariantList &values, const Qg

static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
return executeGeomOverlay( values, context, parent, false, indexedFilteredNearest, false);
return executeGeomOverlay( values, context, parent, false, indexedFilteredNearest, false );
}

const QList<QgsExpressionFunction *> &QgsExpression::Functions()
Expand Down Expand Up @@ -5455,7 +5474,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, -1 ),
fcnGeomOverlayIntersects, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
// The current feature is accessed for the geometry, so this should not be cached
fcnGeomOverlayIntersectsFunc->setIsStatic( false );
Expand All @@ -5474,7 +5493,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, -1 ),
// TODO: limit param
fcnGeomOverlayContains, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
// The current feature is accessed for the geometry, so this should not be cached
Expand All @@ -5494,7 +5513,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, -1 ),
fcnGeomOverlayCrosses, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
// The current feature is accessed for the geometry, so this should not be cached
fcnGeomOverlayCrossesFunc->setIsStatic( false );
Expand All @@ -5513,7 +5532,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, -1 ),
fcnGeomOverlayEquals, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
// The current feature is accessed for the geometry, so this should not be cached
fcnGeomOverlayEqualsFunc->setIsStatic( false );
Expand All @@ -5532,7 +5551,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, -1 ),
fcnGeomOverlayTouches, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
// The current feature is accessed for the geometry, so this should not be cached
fcnGeomOverlayTouchesFunc->setIsStatic( false );
Expand All @@ -5551,7 +5570,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, -1 ),
fcnGeomOverlayDisjoint, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
// The current feature is accessed for the geometry, so this should not be cached
fcnGeomOverlayDisjointFunc->setIsStatic( false );
Expand All @@ -5570,7 +5589,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, -1 ),
fcnGeomOverlayWithin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
// The current feature is accessed for the geometry, so this should not be cached
fcnGeomOverlayWithinFunc->setIsStatic( false );
Expand All @@ -5589,9 +5608,9 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 )
<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, -1 )
<< QgsExpressionFunction::Parameter( QStringLiteral( "neighbors" ), true, 1 )
<< QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0),
<< QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 ),
//<< QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true ),
fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
// The current feature is accessed for the geometry, so this should not be cached
Expand Down