Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Multipart checks
  • Loading branch information
elpaso authored and nyalldawson committed Nov 29, 2021
1 parent 114fc7c commit df805e0
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 20 deletions.
22 changes: 21 additions & 1 deletion resources/function_help/json/overlay_intersects
Expand Up @@ -28,6 +28,18 @@
"description": "set this to true to build a local spatial index (most of the time, this is unwanted, unless you are working with a particularly slow data provider)",
"optional": true,
"default": "false"
},
{
"arg": "min_area",
"description": "an optional minimum area in current feature squared units for the intersection (if the intersection results in multiple polygons the intersection will be returned if at least one of the polygons has an area greater or equal to the value)",
"optional": true,
"default": "false"
},
{
"arg": "min_inscribed_circle_radius",
"description": "an optional minimum radius for the maximum inscribed circle of the intersection (if the intersection results in multiple polygons the intersection will be returned if at least one of the polygons has a radius for the maximum inscribed circle greater or equal to the value)",
"optional": true,
"default": "false"
}
],
"examples": [
Expand All @@ -53,7 +65,15 @@
},
{
"expression": "overlay_intersects(layer:='regions', expression:= geom_to_wkt($geometry), limit:=2)",
"returns": "an array of geometries (in WKT), for up to two regions intersected by the current feature"
"returns": "an array of geometries (in WKT), for up to two regions intersected by the current feature"
},
{
"expression": "overlay_intersects(layer:='regions', min_area:=0.54)",
"returns": "true if the current feature spatially intersects a region and the intersection area (of at least on of the part in case of multipolygons) is greater or equal to the value"
},
{
"expression": "overlay_intersects(layer:='regions', min_inscribed_circle_radius:=0.54)",
"returns": "true if the current feature spatially intersects a region and the intersection area maximum inscribed circle's radius (of at least on of the part in case of multipolygons) is greater or equal to the value"
}
]
}
72 changes: 60 additions & 12 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -6756,26 +6756,74 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
if ( isIntersectsFunc && ( minArea != -1 || minInscribedCircleRadius != -1 ) )
{
const QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
// Check min area for intersection (if set)
// qDebug() << feat2.id() << intersection.area();
if ( minArea != -1 && intersection.area() <= minArea )

// Skip id not polygon
if ( intersection.type() != QgsWkbTypes::GeometryType::PolygonGeometry )
{
continue;
}

// Check min inscribed circle radius for intersection (if set)
if ( minInscribedCircleRadius != -1 )
// Check min area for intersection (if set)
if ( intersection.isMultipart() )
{
bool testResult { false };
for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
{
const QgsPolygon *geom = qgsgeometry_cast< const QgsPolygon * >( *it );
// qDebug() << "Area" << feat2.id() << geom->area();
if ( minArea != -1 )
{
if ( geom->area() >= minArea )
{
testResult = true;
}
else
{
continue;
}
}

// Check min inscribed circle radius for intersection (if set)
if ( minInscribedCircleRadius != -1 )
{
const QgsRectangle bbox = geom->boundingBox();
const double width = bbox.width();
const double height = bbox.height();
const double size = width > height ? width : height;
const double tolerance = size / 1000.0;
//qDebug() << "Inscribed circle radius" << feat2.id() << QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
testResult = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length() >= minInscribedCircleRadius;
}
}

if ( ! testResult )
{
continue;
}

}
else
{
const QgsAbstractGeometry *geom { intersection.constGet() };
const QgsRectangle bbox = geom->boundingBox();
const double width = bbox.width();
const double height = bbox.height();
const double size = width > height ? width : height;
const double tolerance = size / 1000.0;
if ( QgsGeos( geom ).maximumInscribedCircle( tolerance )->length() / 2 < minInscribedCircleRadius )
if ( minArea != -1 && intersection.area() < minArea )
{
continue;
}

// Check min inscribed circle radius for intersection (if set)
if ( minInscribedCircleRadius != -1 )
{
const QgsAbstractGeometry *geom { intersection.constGet() };
const QgsRectangle bbox = geom->boundingBox();
const double width = bbox.width();
const double height = bbox.height();
const double size = width > height ? width : height;
const double tolerance = size / 1000.0;
// qDebug() << "Inscribed circle radius" << feat2.id() << QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
if ( QgsGeos( geom ).maximumInscribedCircle( tolerance )->length() < minInscribedCircleRadius )
{
continue;
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgsgeos.h
Expand Up @@ -297,7 +297,7 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
QString *errorMsg = nullptr ) const;

/**
* Returns the maximum inscribed circle.
* Returns the maximum inscribed circle radius.
*
* Constructs the Maximum Inscribed Circle for a polygonal geometry, up to a specified tolerance.
* The Maximum Inscribed Circle is determined by a point in the interior of the area
Expand Down
36 changes: 30 additions & 6 deletions tests/src/core/testqgsoverlayexpression.cpp
Expand Up @@ -116,7 +116,6 @@ void TestQgsOverlayExpression::testOverlay_data()
QTest::addColumn<QString>( "geometry" );
QTest::addColumn<bool>( "expectedResult" );

/*
QTest::newRow( "intersects" ) << "overlay_intersects('rectangles')" << "POLYGON((-120 30, -105 30, -105 20, -120 20, -120 30))" << true;
QTest::newRow( "intersects [cached]" ) << "overlay_intersects('rectangles',cache:=true)" << "POLYGON((-120 30, -105 30, -105 20, -120 20, -120 30))" << true;

Expand Down Expand Up @@ -155,13 +154,38 @@ void TestQgsOverlayExpression::testOverlay_data()

QTest::newRow( "disjoint no match" ) << "overlay_disjoint('rectangles')" << "LINESTRING(-155 15, -122 32, -84 4)" << false;
QTest::newRow( "disjoint no match [cached]" ) << "overlay_disjoint('rectangles',cache:=true)" << "LINESTRING(-155 15, -122 32, -84 4)" << false;
*/

QTest::newRow( "intersects min_area no match" ) << "overlay_intersects('polys', min_area:=1.3)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << false;
QTest::newRow( "intersects min_area match" ) << "overlay_intersects('polys', min_area:=1.28)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << true;
// Multi part intersection
QTest::newRow( "intersects min_area multi no match" ) << "overlay_intersects('polys', min_area:=1.5)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << false;
QTest::newRow( "intersects min_area multi match" ) << "overlay_intersects('polys', min_area:=1.34)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << true;

QTest::newRow( "intersects min_inscribed_circle_radius no match" ) << "overlay_intersects('polys', min_inscribed_circle_radius:=1.0)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << false;
QTest::newRow( "intersects min_inscribed_circle_radius match" ) << "overlay_intersects('polys', min_area:=0.457)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << true;
QTest::newRow( "intersects min_inscribed_circle_radius multi no match" ) << "overlay_intersects('polys', min_inscribed_circle_radius:=1.0)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << false;
QTest::newRow( "intersects min_inscribed_circle_radius multi match" ) << "overlay_intersects('polys', min_inscribed_circle_radius:=0.5)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << true;

// Single part intersection
QTest::newRow( "intersects min_area no match" ) << "overlay_intersects('polys', min_area:=1.5)" << "POLYGON((-105 33.75, -102.76 33.75, -102.76 35.2, -105 35.2, -105 33.75))" << false;
QTest::newRow( "intersects min_area match" ) << "overlay_intersects('polys', min_area:=1.34)" << "POLYGON((-105 33.75, -102.76 33.75, -102.76 35.2, -105 35.2, -105 33.75))" << true;

QTest::newRow( "intersects min_inscribed_circle_radius no match" ) << "overlay_intersects('polys', min_inscribed_circle_radius:=1.0)" << "POLYGON((-105 33.75, -102.76 33.75, -102.76 35.2, -105 35.2, -105 33.75))" << false;
QTest::newRow( "intersects min_inscribed_circle_radius match" ) << "overlay_intersects('polys', min_inscribed_circle_radius:=0.5)" << "POLYGON((-105 33.75, -102.76 33.75, -102.76 35.2, -105 35.2, -105 33.75))" << true;

// Test both checks combined: they must all pass
// Multi part intersection
QTest::newRow( "intersects multi combined no match 1" ) << "overlay_intersects('polys', min_area:=1.5, min_inscribed_circle_radius:=1.0)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << false;
QTest::newRow( "intersects multi combined no match 2" ) << "overlay_intersects('polys', min_area:=1.5, min_inscribed_circle_radius:=0.5)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << false;

QTest::newRow( "intersects multi combined no match 3" ) << "overlay_intersects('polys', min_area:=1.34, min_inscribed_circle_radius:=1.0)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << false;
QTest::newRow( "intersects multi combined match" ) << "overlay_intersects('polys', min_area:=1.34, min_inscribed_circle_radius:=0.5)" << "POLYGON((-107.37 33.75, -102.76 33.75, -102.76 36.97, -107.37 36.97, -107.37 33.75))" << true;

// Single part intersection
QTest::newRow( "intersects combined no match 1" ) << "overlay_intersects('polys', min_area:=1.5, min_inscribed_circle_radius:=1.0)" << "POLYGON((-105 33.75, -102.76 33.75, -102.76 35.2, -105 35.2, -105 33.75))" << false;
QTest::newRow( "intersects combined no match 2" ) << "overlay_intersects('polys', min_area:=1.34, min_inscribed_circle_radius:=1.0)" << "POLYGON((-105 33.75, -102.76 33.75, -102.76 35.2, -105 35.2, -105 33.75))" << false;
QTest::newRow( "intersects combined no match 3" ) << "overlay_intersects('polys', min_area:=1.5, min_inscribed_circle_radius:=0.5)" << "POLYGON((-105 33.75, -102.76 33.75, -102.76 35.2, -105 35.2, -105 33.75))" << false;

QTest::newRow( "intersects combined match" ) << "overlay_intersects('polys', min_area:=1.34, min_inscribed_circle_radius:=0.5)" << "POLYGON((-105 33.75, -102.76 33.75, -102.76 35.2, -105 35.2, -105 33.75))" << true;

// Check wrong args
QTest::newRow( "intersects wrong args" ) << "overlay_intersects('polys', min_area:=1.5, min_inscribed_circle_radius:=0.5)" << "LINESTRING(-105 33.75, -102.76 33.75)" << false;
}

void TestQgsOverlayExpression::testOverlayExpression()
Expand Down

0 comments on commit df805e0

Please sign in to comment.