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

overlay_intersects: flatten collections and filter by type #59477

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
60 changes: 60 additions & 0 deletions tests/src/core/testqgsoverlayexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class TestQgsOverlayExpression: public QObject
void testOverlayMeasure();
void testOverlayMeasure_data();

void testOverlayIntersectsDetails();

};


Expand Down Expand Up @@ -405,6 +407,64 @@ 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
Loading