Skip to content

Commit

Permalink
Merge pull request #59822 from qgis/backport-59477-to-release-3_40
Browse files Browse the repository at this point in the history
[Backport release-3_40] overlay_intersects: flatten collections and filter by type
  • Loading branch information
qgis-bot authored Jan 12, 2025
1 parent 44bb6ea commit a04cc6b
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 3 deletions.
2 changes: 1 addition & 1 deletion resources/function_help/json/overlay_intersects
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
},
{
"arg": "return_details",
"description": "Set this to true to return a list of maps containing (key names in quotes) the feature 'id', the expression 'result' and the 'overlap' value. The 'radius' of the maximum inscribed circle is also returned when the target layer is a polygon. Only valid when used with the expression parameter",
"description": "Set this to true to return a list of maps containing (key names in quotes) the feature 'id', the expression 'result' and the 'overlap' value (of the largest element in case of multipart). The 'radius' of the maximum inscribed circle is also returned when the target layer is a polygon. Only valid when used with the expression parameter",
"optional": true
},
{
Expand Down
66 changes: 64 additions & 2 deletions src/core/expression/qgsexpressionfunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7951,7 +7951,8 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
bool testResult { false };
// For return measures:
QVector<double> overlapValues;
for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
const QgsGeometry merged { intersection.mergeLines() };
for ( auto it = merged.const_parts_begin(); ! testResult && it != merged.const_parts_end(); ++it )
{
const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
// Check min overlap for intersection (if set)
Expand Down Expand Up @@ -8059,7 +8060,68 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress

if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
{
const QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };

QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };

// Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
{
const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
intersection = QgsGeometry();
QgsMultiPolygonXY poly;
QgsMultiPolylineXY line;
QgsMultiPointXY point;
for ( const auto &geom : std::as_const( geometries ) )
{
switch ( geom.type() )
{
case Qgis::GeometryType::Polygon:
{
poly.append( geom.asPolygon() );
break;
}
case Qgis::GeometryType::Line:
{
line.append( geom.asPolyline() );
break;
}
case Qgis::GeometryType::Point:
{
point.append( geom.asPoint() );
break;
}
case Qgis::GeometryType::Unknown:
case Qgis::GeometryType::Null:
{
break;
}
}
}

switch ( geometry.type() )
{
case Qgis::GeometryType::Polygon:
{
intersection = QgsGeometry::fromMultiPolygonXY( poly );
break;
}
case Qgis::GeometryType::Line:
{
intersection = QgsGeometry::fromMultiPolylineXY( line );
break;
}
case Qgis::GeometryType::Point:
{
intersection = QgsGeometry::fromMultiPointXY( point );
break;
}
case Qgis::GeometryType::Unknown:
case Qgis::GeometryType::Null:
{
break;
}
}
}

// Depending on the intersection geometry type and on the geometry type of
// the tested geometry we can run different tests and collect different measures
Expand Down
59 changes: 59 additions & 0 deletions tests/src/core/testqgsoverlayexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class TestQgsOverlayExpression : public QObject

void testOverlayMeasure();
void testOverlayMeasure_data();

void testOverlayIntersectsDetails();
};


Expand Down Expand Up @@ -393,6 +395,63 @@ void TestQgsOverlayExpression::testOverlayMeasure_data()
QTest::newRow( "intersects line expression no match" ) << "overlay_intersects('polygons2', expression:=fid, return_details:=true, min_inscribed_circle_radius:=3, sort_by_intersection_size:='desc')" << "Polygon ((2604689.01899999985471368 1231313.05799999996088445, 2604695.41300000017508864 1231337.88999999989755452, 2604704.85499999998137355 1231335.10299999988637865, 2604713.89399999985471368 1231333.42900000000372529, 2604719.80599999986588955 1231332.34700000006705523, 2604713.325999999884516 1231305.375, 2604697.20899999979883432 1231310.25600000005215406, 2604689.01899999985471368 1231313.05799999996088445))" << ( QVariantList() << expectedPoly );
}

void TestQgsOverlayExpression::testOverlayIntersectsDetails()
{
// Create polygon memory layer 1
QgsVectorLayer *poly1 = new QgsVectorLayer( QStringLiteral( "Polygon?crs=epsg:4326" ), QStringLiteral( "poly_details" ), QStringLiteral( "memory" ) );
QgsFeature f1;
f1.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((0 0, 0 6, 6 6, 6 0, 0 0))" ) ) );
poly1->dataProvider()->addFeature( f1 );
QgsProject::instance()->addMapLayer( poly1 );

// Create linestring memory layer
QgsVectorLayer *line = new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:4326" ), QStringLiteral( "line_details" ), QStringLiteral( "memory" ) );
QgsFeature f2;
f2.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 0, 1 1, 2 1, 3 1, 4 0)" ) ) );
line->dataProvider()->addFeature( f2 );
QgsProject::instance()->addMapLayer( line );

QgsExpressionContext context;
QgsFeature feat;
feat.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((6 0, 6 2, 4 2, 4 4, 6 4, 6 6, 12 6, 12 0, 6 0))" ) ) );
context.setFeature( feat );
context.appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) );

QgsExpression exp( QStringLiteral( "overlay_intersects('poly_details', return_details:=true, expression:=$id)" ) );
QVERIFY2( exp.prepare( &context ), exp.parserErrorString().toUtf8().constData() );
QVariantList result = exp.evaluate( &context ).toList();
QCOMPARE( result.size(), 1 );
QCOMPARE( result.at( 0 ).toMap().value( QStringLiteral( "id" ) ).toLongLong(), 1 );
QCOMPARE( result.at( 0 ).toMap().value( QStringLiteral( "overlap" ) ).toLongLong(), 4 );

feat.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((7 0, 7 1, 5 1, 5 2, 7 2, 7 3, 4 3, 4 5, 7 5, 7 6, 12 6, 12 0, 7 0))" ) ) );
context.setFeature( feat );
result = exp.evaluate( &context ).toList();
QCOMPARE( result.size(), 1 );
QCOMPARE( result.at( 0 ).toMap().value( QStringLiteral( "id" ) ).toLongLong(), 1 );
QCOMPARE( result.at( 0 ).toMap().value( QStringLiteral( "overlap" ) ).toLongLong(), 4 );

feat.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((6 0, 6 1, 5 1, 5 2, 6 2, 6 3, 4 3, 4 5, 6 5, 6 6, 12 6, 12 0, 6 0))" ) ) );
context.setFeature( feat );
result = exp.evaluate( &context ).toList();
QCOMPARE( result.size(), 1 );
QCOMPARE( result.at( 0 ).toMap().value( QStringLiteral( "id" ) ).toLongLong(), 1 );
QCOMPARE( result.at( 0 ).toMap().value( QStringLiteral( "overlap" ) ).toLongLong(), 4 );

// Test linestring
QgsExpression exp2( QStringLiteral( "overlay_intersects('line_details', return_details:=true, expression:=$id)" ) );
feat.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString (0 2, 1 1, 2 1, 3 1, 4 2)" ) ) );
context.setFeature( feat );
QVERIFY2( exp2.prepare( &context ), exp2.parserErrorString().toUtf8().constData() );
result = exp2.evaluate( &context ).toList();
QCOMPARE( result.size(), 1 );
QCOMPARE( result.at( 0 ).toMap().value( QStringLiteral( "id" ) ).toLongLong(), 1 );
QCOMPARE( result.at( 0 ).toMap().value( QStringLiteral( "overlap" ) ).toLongLong(), 2 );

QgsProject::instance()->removeMapLayer( poly1->id() );
QgsProject::instance()->removeMapLayer( line->id() );
}

void TestQgsOverlayExpression::testOverlayExpression()
{
QFETCH( QString, expression );
Expand Down

0 comments on commit a04cc6b

Please sign in to comment.