Nan z values should not be clipped from rendered geometries

Fixes #51796
This commit is contained in:
Nyall Dawson 2023-02-23 11:03:17 +10:00
parent 65afa1c53b
commit 8f76a6a092
4 changed files with 81 additions and 2 deletions

View File

@ -566,9 +566,9 @@ inline bool QgsClipper::inside( double x, double y, double z, Boundary boundary,
case YMin: // y > MIN_Y is inside
return ( y > boundaryValue );
case ZMax: // z < MAX_Z is inside
return ( z < boundaryValue );
return z < boundaryValue || std::isnan( z ) ;
case ZMin: // z > MIN_Z is inside
return ( z > boundaryValue );
return z > boundaryValue || std::isnan( z );
}
return false;
}

View File

@ -42,6 +42,7 @@ class TestQgsClipper: public QgsTest
void basicWithZ();
void basicWithZInf();
void epsg4978LineRendering();
void clipGeometryWithNaNZValues();
private:
@ -202,6 +203,50 @@ void TestQgsClipper::epsg4978LineRendering()
QVERIFY( render2dCheck( "4978_2D_line_rendering", layerLines.get(), QgsRectangle( -2.5e7, -2.5e7, 2.5e7, 2.5e7 ) ) );
}
void TestQgsClipper::clipGeometryWithNaNZValues()
{
// nan z values should not result in clipping
QgsGeometry geom = QgsGeometry::fromWkt( QStringLiteral( "PolygonZ ((704425.82266869 7060014.33574043 19.51, 704439.59844559 7060023.73007711 19.69, 704441.6748229 7060020.65665367 19.63, 704428.333268 7060011.65915509 19.42, 704428.15434668 7060011.92446088 0, 704441.23037799 7060020.74289127 0, 704439.51320673 7060023.28462315 0, 704426.00295955 7060014.07136342 0, 704425.82266869 7060014.33574043 19.51))" ) );
QgsLineString *exteriorRing = qgsgeometry_cast< QgsLineString * >( qgsgeometry_cast< QgsPolygon *>( geom.get() )->exteriorRing() );
exteriorRing->setZAt( 4, std::numeric_limits<double>::quiet_NaN() );
exteriorRing->setZAt( 5, std::numeric_limits<double>::quiet_NaN() );
exteriorRing->setZAt( 6, std::numeric_limits<double>::quiet_NaN() );
exteriorRing->setZAt( 7, std::numeric_limits<double>::quiet_NaN() );
QPolygonF polygon;
polygon << QPointF( 10.4, 20.5 ) << QPointF( 20.2, 30.2 );
QVector< double > pointsX = exteriorRing->xVector();
QVector< double > pointsY = exteriorRing->yVector();
QVector< double > pointsZ = exteriorRing->zVector();
QCOMPARE( pointsX.size(), 9 );
// 2d trim
QgsRectangle clipRect( 704430.30, 7060007.72, 704456.51, 7060029.03 );
QgsClipper::trimPolygon( pointsX, pointsY, pointsZ, clipRect );
// one vertex should be clipped
QCOMPARE( pointsX.size(), 8 );
QCOMPARE( pointsY.size(), 8 );
QCOMPARE( pointsZ.size(), 8 );
QGSCOMPARENEAR( pointsX.at( 0 ), 704430.30, 0.01 );
QGSCOMPARENEAR( pointsY.at( 0 ), 7060017.389, 0.01 );
QGSCOMPARENEAR( pointsZ.at( 0 ), 19.568, 0.01 );
// 3d trim, clip box contains whole geometry
pointsX = exteriorRing->xVector();
pointsY = exteriorRing->yVector();
pointsZ = exteriorRing->zVector();
QgsBox3d clipRect3D( 704430.30, 7060007.72, 0, 704456.51, 7060029.03, 100 );
QgsClipper::trimPolygon( pointsX, pointsY, pointsZ, clipRect3D );
// still only one vertex should be clipped, the nan z values must remain
QCOMPARE( pointsX.size(), 8 );
QCOMPARE( pointsY.size(), 8 );
QCOMPARE( pointsZ.size(), 8 );
QGSCOMPARENEAR( pointsX.at( 0 ), 704430.30, 0.01 );
QGSCOMPARENEAR( pointsY.at( 0 ), 7060017.389, 0.01 );
QGSCOMPARENEAR( pointsZ.at( 0 ), 19.568, 0.01 );
}
bool TestQgsClipper::render2dCheck( const QString &testName, QgsVectorLayer *layer, QgsRectangle extent )
{
const QString myTmpDir = QDir::tempPath() + '/';

View File

@ -483,6 +483,40 @@ class TestQgsGeometryGeneratorSymbolLayerV2(unittest.TestCase):
self.report += renderchecker.report()
self.assertTrue(res)
def test_clipped_results_with_z(self):
"""
See https://github.com/qgis/QGIS/issues/51796
"""
lines = QgsVectorLayer('LineString?crs=epsg:2154', 'Lines', 'memory')
self.assertTrue(lines.isValid())
f = QgsFeature()
f.setGeometry(QgsGeometry.fromWkt('LineStringZ (704425.82266868802253157 7060014.33574043028056622 19.51000000000000156, 704439.59844558802433312 7060023.7300771102309227 19.69000000000000128, 704441.67482289997860789 7060020.65665366966277361 19.62999999999999901, 704428.333267995971255 7060011.65915509033948183 19.42000000000000171)'))
lines.dataProvider().addFeature(f)
subsymbol = QgsFillSymbol.createSimple({'color': '#0000ff', 'line_style': 'no'})
parent_generator = QgsGeometryGeneratorSymbolLayer.create(
{'geometryModifier': 'single_sided_buffer($geometry,-0.32, 1, 2)'})
parent_generator.setSymbolType(QgsSymbol.Fill)
parent_generator.setSubSymbol(subsymbol)
geom_symbol = QgsLineSymbol()
geom_symbol.changeSymbolLayer(0, parent_generator)
lines.renderer().setSymbol(geom_symbol)
mapsettings = QgsMapSettings(self.mapsettings)
mapsettings.setDestinationCrs(lines.crs())
mapsettings.setExtent(QgsRectangle(704433.77, 7060006.64, 704454.78, 7060027.95))
mapsettings.setLayers([lines])
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlName('expected_geometrygenerator_z_clipping')
res = renderchecker.runTest('geometrygenerator_z_clipping')
self.report += renderchecker.report()
self.assertTrue(res)
def imageCheck(self, name, reference_image, image):
self.report += f"<h2>Render {name}</h2>\n"
temp_dir = QDir.tempPath() + '/'

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB