Fix unreported issue with polygon intersection resulting in a point with min_inscribed_circle_radius and expression

The issue was that in case of intersections resulting in a single point
and when meausres were requires (i.e. when sorting) the circle radius
test result was ignored.
This commit is contained in:
Alessandro Pasotti 2022-06-14 18:29:42 +02:00
parent 10284637af
commit c40516b06d
5 changed files with 117 additions and 84 deletions

View File

@ -6953,6 +6953,105 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
}
}
// //////////////////////////////////////////////////////////////////
// Helper functions for geometry tests
// Test function for linestring geometries, returns TRUE if test passes
auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
{
bool testResult { false };
// For return measures:
QVector<double> overlapValues;
for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
{
const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
// Check min overlap for intersection (if set)
if ( minOverlap != -1 || requireMeasures )
{
overlapValue = geom->length();
overlapValues.append( overlapValue );
if ( minOverlap != -1 )
{
if ( overlapValue >= minOverlap )
{
testResult = true;
}
else
{
continue;
}
}
}
}
if ( ! overlapValues.isEmpty() )
{
overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
}
return testResult;
};
// Test function for polygon geometries, returns TRUE if test passes
auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
{
// overlap and inscribed circle tests must be checked both (if the values are != -1)
bool testResult { false };
// For return measures:
QVector<double> overlapValues;
QVector<double> radiusValues;
for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
{
const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
// Check min overlap for intersection (if set)
if ( minOverlap != -1 || requireMeasures )
{
overlapValue = geom->area();
overlapValues.append( geom->area() );
if ( minOverlap != - 1 )
{
if ( overlapValue >= minOverlap )
{
testResult = true;
}
else
{
continue;
}
}
}
#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=9 )
// Check min inscribed circle radius for intersection (if set)
if ( minInscribedCircleRadius != -1 || requireMeasures )
{
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 / 100.0;
radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
testResult = radiusValue >= minInscribedCircleRadius;
radiusValues.append( radiusValues );
}
#endif
} // end for parts
// Get the max values
if ( !radiusValues.isEmpty() )
{
radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
}
if ( ! overlapValues.isEmpty() )
{
overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
}
return testResult;
};
bool found = false;
int foundCount = 0;
@ -6961,8 +7060,10 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
QListIterator<QgsFeature> i( features );
while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
{
QgsFeature feat2 = i.next();
if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
{
@ -6983,57 +7084,7 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
{
// overlap and inscribed circle tests must be checked both (if the values are != -1)
bool testResult { false };
// For return measures:
QVector<double> overlapValues;
QVector<double> radiusValues;
for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
{
const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
// Check min overlap for intersection (if set)
if ( minOverlap != -1 || requireMeasures )
{
overlapValue = geom->area();
overlapValues.append( geom->area() );
if ( minOverlap != - 1 )
{
if ( overlapValue >= minOverlap )
{
testResult = true;
}
else
{
continue;
}
}
}
#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=9 )
// Check min inscribed circle radius for intersection (if set)
if ( minInscribedCircleRadius != -1 || requireMeasures )
{
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 / 100.0;
radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
testResult = radiusValue >= minInscribedCircleRadius;
radiusValues.append( radiusValues );
}
#endif
}
// Get the max values
if ( !radiusValues.isEmpty() )
{
radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
}
if ( ! overlapValues.isEmpty() )
{
overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
}
bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
if ( ! testResult && overlapOrRadiusFilter )
{
@ -7042,37 +7093,11 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
break;
}
case QgsWkbTypes::GeometryType::LineGeometry:
{
bool testResult { false };
// For return measures:
QVector<double> overlapValues;
for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
{
const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
// Check min overlap for intersection (if set)
if ( minOverlap != -1 || requireMeasures )
{
overlapValue = geom->length();
overlapValues.append( overlapValue );
if ( minOverlap != -1 )
{
if ( overlapValue >= minOverlap )
{
testResult = true;
}
else
{
continue;
}
}
}
}
if ( ! overlapValues.isEmpty() )
{
overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
}
const bool testResult { testLinestring( intersection, overlapValue ) };
if ( ! testResult && overlapOrRadiusFilter )
{
@ -7081,9 +7106,11 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
break;
}
case QgsWkbTypes::GeometryType::PointGeometry:
{
if ( minOverlap != -1 || requireMeasures )
bool testResult { false };
if ( minOverlap != -1 || requireMeasures )
{
// Initially set this to 0 because it's a point intersection...
overlapValue = 0;
@ -7103,18 +7130,18 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
}
case QgsWkbTypes::GeometryType::LineGeometry:
{
overlapValue = feat2.geometry().length();
testResult = testLinestring( feat2.geometry(), overlapValue );
break;
}
case QgsWkbTypes::GeometryType::PolygonGeometry:
{
overlapValue = feat2.geometry().area();
testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
break;
}
}
}
if ( minOverlap != -1 && overlapValue < minOverlap )
if ( ! testResult && overlapOrRadiusFilter )
{
continue;
}
@ -7122,6 +7149,7 @@ static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpress
}
break;
}
case QgsWkbTypes::GeometryType::NullGeometry:
case QgsWkbTypes::GeometryType::UnknownGeometry:
{

View File

@ -297,10 +297,12 @@ void TestQgsOverlayExpression::testOverlayMeasure_data()
expected3.insert( QStringLiteral( "id" ), 3LL );
expected3.insert( QStringLiteral( "result" ), 3 );
expected3.insert( QStringLiteral( "overlap" ), 19.049688572712284 );
expected3.insert( QStringLiteral( "radius" ), 1.3414663642343596 );
QVariantMap expected1;
expected1.insert( QStringLiteral( "id" ), 1LL );
expected1.insert( QStringLiteral( "result" ), 1 );
expected1.insert( QStringLiteral( "overlap" ), 18.569843123334977 );
expected1.insert( QStringLiteral( "radius" ), 1.8924012738149243 );
QTest::newRow( "intersects multi match points return sorted" ) << "overlay_intersects('polys', sort_by_intersection_size:='des', return_details:=true, expression:=$id)" << "MULTIPOINT((-107.37 33.75), (-102.8 36.97))" << ( QVariantList() << expected3 << expected1 ) ;
@ -338,7 +340,6 @@ void TestQgsOverlayExpression::testOverlayMeasure_data()
QTest::newRow( "intersects linestring multi match measure sorted" ) << "overlay_intersects('polys', return_details:=true, sort_by_intersection_size:='des', expression:=$id)" << "LINESTRING(-102.76 33.74, -102.76 36.44)" << ( QVariantList() << expectedLine3 << expectedLine1 );
// Test linestring intersections
{
QVariantMap expectedLine1;
@ -351,6 +352,8 @@ void TestQgsOverlayExpression::testOverlayMeasure_data()
expectedLine2.insert( QStringLiteral( "overlap" ), 0 );
QTest::newRow( "intersects linestring single match" ) << "overlay_intersects('linestrings', return_details:=true, expression:=$id)" << "LINESTRING(1.5 1, 1.5 -1)" << ( QVariantList() << expectedLine1 );
QTest::newRow( "intersects linestring single match fail overlap" ) << "overlay_intersects('linestrings', min_overlap:=0.5, return_details:=true, expression:=$id)" << "LINESTRING(1.5 1, 1.5 -1)" << ( QVariantList() );
QTest::newRow( "intersects linestring no match" ) << "overlay_intersects('linestrings', return_details:=true, expression:=$id)" << "LINESTRING(1.5 2, 1.5 1)" << ( QVariantList() );
QTest::newRow( "intersects linestring multi match" ) << "overlay_intersects('linestrings', return_details:=true, expression:=$id)" << "LINESTRING(1.5 1, 1.5 -1, 4 -1, 4 1)" << ( QVariantList() << expectedLine1 << expectedLine2 );
@ -381,6 +384,8 @@ void TestQgsOverlayExpression::testOverlayMeasure_data()
QTest::newRow( "intersects points multi match" ) << "overlay_intersects('points', return_details:=true, expression:=$id)" << "POLYGON((0 -1, 3.5 -1, 3.5 1, 0 1, 0 -1))" << ( QVariantList() << expectedPoint1 << expectedPoint2 );
}
// Test polygon intersection resulting in a point with min_inscribed_circle_radius and expression
QTest::newRow( "intersects point expression no match" ) << "overlay_intersects('polys', expression:=$id, min_inscribed_circle_radius:=0.5, sort_by_intersection_size:='desc')" << "POLYGON((-90.825 34.486, -89.981 35.059, -90.009 33.992, -90.825 34.486))" << ( QVariantList() );
}
void TestQgsOverlayExpression::testOverlayExpression()

Binary file not shown.

Binary file not shown.

Binary file not shown.