Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Correctly transform on the fly point clouds while rendering in 2d
and layer crs differs from map crs
  • Loading branch information
nyalldawson committed Nov 27, 2020
1 parent c0a579c commit 033bcce
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 13 deletions.
5 changes: 5 additions & 0 deletions src/core/pointcloud/qgspointcloudindex.cpp
Expand Up @@ -218,3 +218,8 @@ void QgsPointCloudIndex::setAttributes( const QgsPointCloudAttributeCollection &
{
mAttributes = attributes;
}

int QgsPointCloudIndex::span() const
{
return mSpan;
}
5 changes: 5 additions & 0 deletions src/core/pointcloud/qgspointcloudindex.h
Expand Up @@ -194,6 +194,11 @@ class CORE_EXPORT QgsPointCloudIndex: public QObject
//! Returns offset
QgsVector3D offset() const;

/**
* Returns the number of points in one direction in a single node.
*/
int span() const;

protected: //TODO private
//! Sets native attributes of the data
void setAttributes( const QgsPointCloudAttributeCollection &attributes );
Expand Down
32 changes: 23 additions & 9 deletions src/core/pointcloud/qgspointcloudlayerrenderer.cpp
Expand Up @@ -75,7 +75,6 @@ bool QgsPointCloudLayerRenderer::render()
mAttributes.push_back( mLayer->attributes().at( layerIndex ) );
}


// Set up the render configuration options
QPainter *painter = context.renderContext().painter();

Expand All @@ -91,14 +90,27 @@ bool QgsPointCloudLayerRenderer::render()

const float maximumError = context.renderContext().convertToPainterUnits( mRenderer->maximumScreenError(), mRenderer->maximumScreenErrorUnit() );// in pixels

float rootError = pc->nodeError( root ); // in map coords
const QgsRectangle rootNodeExtentLayerCoords = pc->nodeMapExtent( root );
QgsRectangle rootNodeExtentMapCoords;
try
{
rootNodeExtentMapCoords = context.renderContext().coordinateTransform().transformBoundingBox( rootNodeExtentLayerCoords );
}
catch ( QgsCsException & )
{
QgsDebugMsg( QStringLiteral( "Could not transform node extent to map CRS" ) );
rootNodeExtentMapCoords = rootNodeExtentLayerCoords;
}

const float rootErrorInMapCoordinates = rootNodeExtentMapCoords.width() / pc->span(); // in map coords

double mapUnitsPerPixel = context.renderContext().mapToPixel().mapUnitsPerPixel();
if ( ( rootError < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumError < 0.0 ) )
if ( ( rootErrorInMapCoordinates < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumError < 0.0 ) )
{
qDebug() << "invalid screen error";
QgsDebugMsg( QStringLiteral( "invalid screen error" ) );
return false;
}
float rootErrorPixels = rootError / mapUnitsPerPixel; // in pixels
float rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels
const QList<IndexedPointCloudNode> nodes = traverseTree( pc, context.renderContext(), pc->root(), maximumError, rootErrorPixels );

QgsPointCloudRequest request;
Expand All @@ -110,7 +122,7 @@ bool QgsPointCloudLayerRenderer::render()
{
if ( context.renderContext().renderingStopped() )
{
qDebug() << "canceled";
QgsDebugMsgLevel( "canceled", 2 );
break;
}
std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
Expand All @@ -124,7 +136,9 @@ bool QgsPointCloudLayerRenderer::render()
++nodesDrawn;
}

qDebug() << "totals:" << nodesDrawn << "nodes | " << context.pointsRendered() << " points | " << t.elapsed() << "ms";
QgsDebugMsgLevel( QStringLiteral( "totals: %1 nodes | %2 points | %3ms" ).arg( nodesDrawn )
.arg( context.pointsRendered() )
.arg( t.elapsed() ), 2 );

mRenderer->stopRender( context );

Expand All @@ -141,11 +155,11 @@ QList<IndexedPointCloudNode> QgsPointCloudLayerRenderer::traverseTree( const Qgs

if ( context.renderingStopped() )
{
qDebug() << "canceled";
QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
return nodes;
}

if ( !context.mapExtent().intersects( pc->nodeMapExtent( n ) ) )
if ( !context.extent().intersects( pc->nodeMapExtent( n ) ) )
return nodes;

nodes.append( n );
Expand Down
20 changes: 17 additions & 3 deletions src/core/pointcloud/qgspointcloudrgbrenderer.cpp
Expand Up @@ -58,7 +58,7 @@ void QgsPointCloudRgbRenderer::renderBlock( const QgsPointCloudBlock *block, Qgs
{
const QgsMapToPixel mapToPixel = context.renderContext().mapToPixel();

QgsRectangle mapExtent = context.renderContext().mapExtent();
const QgsRectangle visibleExtent = context.renderContext().extent();

QPen pen;
pen.setWidth( mPainterPenWidth );
Expand Down Expand Up @@ -95,12 +95,26 @@ void QgsPointCloudRgbRenderer::renderBlock( const QgsPointCloudBlock *block, Qgs
int rendered = 0;
double x = 0;
double y = 0;
double z = 0;
const QgsCoordinateTransform ct = context.renderContext().coordinateTransform();
const bool reproject = ct.isValid();
for ( int i = 0; i < count; ++i )
{
pointXY( context, ptr, i, x, y );

if ( mapExtent.contains( QgsPointXY( x, y ) ) )
if ( visibleExtent.contains( QgsPointXY( x, y ) ) )
{
if ( reproject )
{
try
{
ct.transformInPlace( x, y, z );
}
catch ( QgsCsException & )
{
continue;
}
}

int red = 0;
switch ( redType )
{
Expand Down
25 changes: 24 additions & 1 deletion tests/src/python/test_qgspointcloudrgbrenderer.py
Expand Up @@ -25,7 +25,8 @@
QgsRectangle,
QgsContrastEnhancement,
QgsUnitTypes,
QgsMapUnitScale
QgsMapUnitScale,
QgsCoordinateReferenceSystem
)

from qgis.PyQt.QtCore import QDir, QSize
Expand Down Expand Up @@ -170,6 +171,28 @@ def testRender(self):
TestQgsPointCloudRgbRenderer.report += renderchecker.report()
self.assertTrue(result)

@unittest.skipIf('ept' not in QgsProviderRegistry.instance().providerList(), 'EPT provider not available')
def testRenderCrsTransform(self):
layer = QgsPointCloudLayer(unitTestDataPath() + '/point_clouds/ept/rgb/ept.json', 'test', 'ept')
self.assertTrue(layer.isValid())

layer.renderer().setPointSize(2)
layer.renderer().setPointSizeUnit(QgsUnitTypes.RenderMillimeters)

mapsettings = QgsMapSettings()
mapsettings.setOutputSize(QSize(400, 400))
mapsettings.setOutputDpi(96)
mapsettings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
mapsettings.setExtent(QgsRectangle(152.977434544, -26.663017454, 152.977424882, -26.663009624))
mapsettings.setLayers([layer])
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('pointcloudrenderer')
renderchecker.setControlName('expected_rgb_render_crs_transform')
result = renderchecker.runTest('expected_rgb_render_crs_transform')
TestQgsPointCloudRgbRenderer.report += renderchecker.report()
self.assertTrue(result)

@unittest.skipIf('ept' not in QgsProviderRegistry.instance().providerList(), 'EPT provider not available')
def testRenderWithContrast(self):
layer = QgsPointCloudLayer(unitTestDataPath() + '/point_clouds/ept/rgb/ept.json', 'test', 'ept')
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 033bcce

Please sign in to comment.