Skip to content

Commit

Permalink
Fix 3D point cloud position when layer CRS != project CRS (#41538)
Browse files Browse the repository at this point in the history
* hopefully fixes CRS problem

* fix the 3D map position shift

* remove qDebug

* add catch throw around transformInPlace

* switch to using QgsCoordinateTransform::transform

* move QgsCoordinateTransform to transform context

* remove TODO and fix layout

* add docs

* rename variable

* hadle bounding box coordinate transformation
  • Loading branch information
NEDJIMAbelgacem committed Feb 18, 2021
1 parent a89241c commit d80c27d
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 23 deletions.
14 changes: 14 additions & 0 deletions python/core/auto_generated/qgscoordinatetransform.sip.in
Expand Up @@ -213,6 +213,20 @@ otherwise points are transformed from destination to source CRS.
:param direction: transform direction (defaults to ForwardTransform)

:return: transformed point
%End

QgsVector3D transform( const QgsVector3D &point, TransformDirection direction = ForwardTransform ) const;
%Docstring
Transform the point specified in 3D coordinates from the source CRS to the destination CRS.
If the direction is ForwardTransform then coordinates are transformed from source to destination,
otherwise points are transformed from destination to source CRS.

:param point: coordinates of point to transform
:param direction: transform direction (defaults to ForwardTransform)

:return: transformed point

.. versionadded:: 3.18
%End

QgsRectangle transformBoundingBox( const QgsRectangle &rectangle, TransformDirection direction = ForwardTransform, bool handle180Crossover = false ) const throw( QgsCsException );
Expand Down
14 changes: 12 additions & 2 deletions src/3d/qgspointcloudlayer3drenderer.cpp
Expand Up @@ -29,11 +29,12 @@

#include "qgis.h"

QgsPointCloud3DRenderContext::QgsPointCloud3DRenderContext( const Qgs3DMapSettings &map, std::unique_ptr<QgsPointCloud3DSymbol> symbol, double zValueScale, double zValueFixedOffset )
QgsPointCloud3DRenderContext::QgsPointCloud3DRenderContext( const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, std::unique_ptr<QgsPointCloud3DSymbol> symbol, double zValueScale, double zValueFixedOffset )
: Qgs3DRenderContext( map )
, mSymbol( std::move( symbol ) )
, mZValueScale( zValueScale )
, mZValueFixedOffset( zValueFixedOffset )
, mCoordinateTransform( coordinateTransform )
{
auto callback = []()->bool
{
Expand Down Expand Up @@ -65,6 +66,11 @@ QSet<int> QgsPointCloud3DRenderContext::getFilteredOutValues() const
return filteredOut;
}

void QgsPointCloud3DRenderContext::setCoordinateTransform( const QgsCoordinateTransform &coordinateTransform )
{
mCoordinateTransform = coordinateTransform;
}

QgsPointCloudLayer3DRendererMetadata::QgsPointCloudLayer3DRendererMetadata()
: Qgs3DRendererAbstractMetadata( QStringLiteral( "pointcloud" ) )
{
Expand Down Expand Up @@ -121,7 +127,9 @@ Qt3DCore::QEntity *QgsPointCloudLayer3DRenderer::createEntity( const Qgs3DMapSet
if ( !mSymbol )
return nullptr;

return new QgsPointCloudLayerChunkedEntity( pcl->dataProvider()->index(), map, dynamic_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() ), mMaximumScreenError, showBoundingBoxes(),
QgsCoordinateTransform coordinateTransform( pcl->crs(), map.crs(), map.transformContext() );

return new QgsPointCloudLayerChunkedEntity( pcl->dataProvider()->index(), map, coordinateTransform, dynamic_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() ), maximumScreenError(), showBoundingBoxes(),
static_cast< const QgsPointCloudLayerElevationProperties * >( pcl->elevationProperties() )->zScale(),
static_cast< const QgsPointCloudLayerElevationProperties * >( pcl->elevationProperties() )->zOffset(), mPointBudget );
}
Expand Down Expand Up @@ -206,3 +214,5 @@ void QgsPointCloudLayer3DRenderer::setPointRenderingBudget( int budget )
{
mPointBudget = budget;
}


13 changes: 12 additions & 1 deletion src/3d/qgspointcloudlayer3drenderer.h
Expand Up @@ -52,7 +52,7 @@ class _3D_NO_EXPORT QgsPointCloud3DRenderContext : public Qgs3DRenderContext
* The \a zValueFixedOffset argument specifies any constant offset value which must be added to z values
* taken from the point cloud index.
*/
QgsPointCloud3DRenderContext( const Qgs3DMapSettings &map, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
QgsPointCloud3DRenderContext( const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
double zValueScale, double zValueFixedOffset );

//! QgsPointCloudRenderContext cannot be copied.
Expand Down Expand Up @@ -160,6 +160,16 @@ class _3D_NO_EXPORT QgsPointCloud3DRenderContext : public Qgs3DRenderContext
*/
bool isCanceled() const { return mIsCanceledCallback(); }

/**
* Sets the coordinate transform used to transform points from layer CRS to the map CRS
*/
void setCoordinateTransform( const QgsCoordinateTransform &coordinateTransform );

/**
* Returns the coordinate transform used to transform points from layer CRS to the map CRS
*/
QgsCoordinateTransform coordinateTransform() const { return mCoordinateTransform; }

private:
#ifdef SIP_RUN
QgsPointCloudRenderContext( const QgsPointCloudRenderContext &rh );
Expand All @@ -169,6 +179,7 @@ class _3D_NO_EXPORT QgsPointCloud3DRenderContext : public Qgs3DRenderContext
QgsPointCloudCategoryList mFilteredOutCategories;
double mZValueScale = 1.0;
double mZValueFixedOffset = 0;
QgsCoordinateTransform mCoordinateTransform;

std::function< bool() > mIsCanceledCallback;

Expand Down
34 changes: 22 additions & 12 deletions src/3d/qgspointcloudlayerchunkloader_p.cpp
Expand Up @@ -47,10 +47,10 @@
///////////////

QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
double zValueScale, double zValueOffset )
const QgsCoordinateTransform &coordinateTransform, double zValueScale, double zValueOffset )
: QgsChunkLoader( node )
, mFactory( factory )
, mContext( factory->mMap, std::move( symbol ), zValueScale, zValueOffset )
, mContext( factory->mMap, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
{
mContext.setIsCanceledCallback( [this] { return mCanceled; } );

Expand Down Expand Up @@ -128,9 +128,10 @@ Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntit
///////////////


QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
double zValueScale, double zValueOffset, int pointBudget )
: mMap( map )
, mCoordinateTransform( coordinateTransform )
, mPointCloudIndex( pc )
, mZValueScale( zValueScale )
, mZValueOffset( zValueOffset )
Expand All @@ -142,9 +143,10 @@ QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( cons
QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
{
QgsChunkNodeId id = node->tileId();
IndexedPointCloudNode n( id.d, id.x, id.y, id.z );
Q_ASSERT( mPointCloudIndex->hasNode( n ) );
return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() ) ), mZValueScale, mZValueOffset );

Q_ASSERT( mPointCloudIndex->hasNode( IndexedPointCloudNode( id.d, id.x, id.y, id.z ) ) );
QgsPointCloud3DSymbol *symbol = static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() );
return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
}

int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node ) const
Expand All @@ -155,11 +157,11 @@ int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node )
return mPointCloudIndex->nodePointCount( n );
}

QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, double zValueOffset );
QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, double zValueOffset );

QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode() const
{
QgsAABB bbox = nodeBoundsToAABB( mPointCloudIndex->nodeBounds( IndexedPointCloudNode( 0, 0, 0, 0 ) ), mPointCloudIndex->offset(), mPointCloudIndex->scale(), mMap, mZValueOffset );
QgsAABB bbox = nodeBoundsToAABB( mPointCloudIndex->nodeBounds( IndexedPointCloudNode( 0, 0, 0, 0 ) ), mPointCloudIndex->offset(), mPointCloudIndex->scale(), mMap, mCoordinateTransform, mZValueOffset );
float error = mPointCloudIndex->nodeError( IndexedPointCloudNode( 0, 0, 0, 0 ) );
return new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), bbox, error );
}
Expand Down Expand Up @@ -198,11 +200,19 @@ QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( Qg
///////////////


QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, double zValueOffset )
QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, double zValueOffset )
{
// TODO: reprojection from layer to map coordinates if needed
QgsVector3D extentMin3D( nodeBounds.xMin() * scale.x() + offset.x(), nodeBounds.yMin() * scale.y() + offset.y(), nodeBounds.zMin() * scale.z() + offset.z() + zValueOffset );
QgsVector3D extentMax3D( nodeBounds.xMax() * scale.x() + offset.x(), nodeBounds.yMax() * scale.y() + offset.y(), nodeBounds.zMax() * scale.z() + offset.z() + zValueOffset );
try
{
extentMin3D = coordinateTransform.transform( extentMin3D );
extentMax3D = coordinateTransform.transform( extentMax3D );
}
catch ( QgsCsException &e )
{
QgsDebugMsg( QStringLiteral( "Error transforming node bounds coordinate" ) );
}
QgsVector3D worldExtentMin3D = Qgs3DUtils::mapToWorldCoordinates( extentMin3D, map.origin() );
QgsVector3D worldExtentMax3D = Qgs3DUtils::mapToWorldCoordinates( extentMax3D, map.origin() );
QgsAABB rootBbox( worldExtentMin3D.x(), worldExtentMin3D.y(), worldExtentMin3D.z(),
Expand All @@ -211,9 +221,9 @@ QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset
}


QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, QgsPointCloud3DSymbol *symbol, double maximumScreenSpaceError, bool showBoundingBoxes, double zValueScale, double zValueOffset, int pointBudget )
QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, QgsPointCloud3DSymbol *symbol, float maximumScreenSpaceError, bool showBoundingBoxes, double zValueScale, double zValueOffset, int pointBudget )
: QgsChunkedEntity( maximumScreenSpaceError,
new QgsPointCloudLayerChunkLoaderFactory( map, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
new QgsPointCloudLayerChunkLoaderFactory( map, coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
{
setUsingAdditiveStrategy( true );
setShowBoundingBoxes( showBoundingBoxes );
Expand Down
8 changes: 5 additions & 3 deletions src/3d/qgspointcloudlayerchunkloader_p.h
Expand Up @@ -59,7 +59,7 @@ class QgsPointCloudLayerChunkLoaderFactory : public QgsChunkLoaderFactory
* Constructs the factory
* The factory takes ownership over the passed \a symbol
*/
QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
double zValueScale, double zValueOffset, int pointBudget );

//! Creates loader for the given chunk node. Ownership of the returned is passed to the caller.
Expand All @@ -68,6 +68,7 @@ class QgsPointCloudLayerChunkLoaderFactory : public QgsChunkLoaderFactory
virtual QVector<QgsChunkNode *> createChildren( QgsChunkNode *node ) const override;
virtual int primitivesCount( QgsChunkNode *node ) const override;
const Qgs3DMapSettings &mMap;
QgsCoordinateTransform mCoordinateTransform;
QgsPointCloudIndex *mPointCloudIndex;
std::unique_ptr< QgsPointCloud3DSymbol > mSymbol;
double mZValueScale = 1.0;
Expand All @@ -92,7 +93,8 @@ class QgsPointCloudLayerChunkLoader : public QgsChunkLoader
* Constructs the loader
* QgsPointCloudLayerChunkLoader takes ownership over symbol
*/
QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol, double zValueScale, double zValueOffset );
QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
const QgsCoordinateTransform &coordinateTransform, double zValueScale, double zValueOffset );
~QgsPointCloudLayerChunkLoader() override;

virtual void cancel() override;
Expand Down Expand Up @@ -121,7 +123,7 @@ class QgsPointCloudLayerChunkedEntity : public QgsChunkedEntity
{
Q_OBJECT
public:
explicit QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, QgsPointCloud3DSymbol *symbol, double maximumScreenSpaceError, bool showBoundingBoxes,
explicit QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, QgsPointCloud3DSymbol *symbol, float maxScreenError, bool showBoundingBoxes,
double zValueScale, double zValueOffset, int pointBudget );

~QgsPointCloudLayerChunkedEntity();
Expand Down
65 changes: 60 additions & 5 deletions src/3d/symbols/qgspointcloud3dsymbol_p.cpp
Expand Up @@ -261,6 +261,8 @@ void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *p
const QgsVector3D offset = pc->offset();
const double zValueScale = context.zValueScale();
const double zValueOffset = context.zValueFixedOffset();
QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
bool alreadyPrintedDebug = false;

for ( int i = 0; i < count; ++i )
{
Expand All @@ -274,8 +276,20 @@ void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *p
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = ( offset.z() + scale.z() * iz ) * zValueScale + zValueOffset;
try
{
coordinateTransform.transformInPlace( x, y, z );
}
catch ( QgsCsException &e )
{
if ( !alreadyPrintedDebug )
{
QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
alreadyPrintedDebug = true;
}
}
QgsVector3D point( x, y, z );
QgsVector3D p = context.map().mapToWorldCoordinates( point );
QgsVector3D p = context.map().mapToWorldCoordinates( QgsVector3D( x, y, z ) );
outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
}
}
Expand Down Expand Up @@ -320,6 +334,9 @@ void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc,
int attributeOffset = 0;
const double zValueScale = context.zValueScale();
const double zValueOffset = context.zValueFixedOffset();
QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
bool alreadyPrintedDebug = false;

QgsColorRampPointCloud3DSymbol *symbol = dynamic_cast<QgsColorRampPointCloud3DSymbol *>( context.symbol() );
if ( symbol )
{
Expand Down Expand Up @@ -379,10 +396,21 @@ void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc,
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = ( offset.z() + scale.z() * iz ) * zValueScale + zValueOffset;
try
{
coordinateTransform.transformInPlace( x, y, z );
}
catch ( QgsCsException &e )
{
if ( !alreadyPrintedDebug )
{
QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
alreadyPrintedDebug = true;
}
}
QgsVector3D point( x, y, z );

QgsVector3D p = context.map().mapToWorldCoordinates( point );
outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
point = context.map().mapToWorldCoordinates( point );
outNormal.positions.push_back( QVector3D( point.x(), point.y(), point.z() ) );

if ( attrIsX )
outNormal.parameter.push_back( x );
Expand Down Expand Up @@ -462,6 +490,8 @@ void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const
const QgsVector3D offset = pc->offset();
const double zValueScale = context.zValueScale();
const double zValueOffset = context.zValueFixedOffset();
QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
bool alreadyPrintedDebug = false;

QgsContrastEnhancement *redContrastEnhancement = symbol->redContrastEnhancement();
QgsContrastEnhancement *greenContrastEnhancement = symbol->greenContrastEnhancement();
Expand All @@ -485,6 +515,18 @@ void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = ( offset.z() + scale.z() * iz ) * zValueScale + zValueOffset;
try
{
coordinateTransform.transformInPlace( x, y, z );
}
catch ( QgsCsException &e )
{
if ( !alreadyPrintedDebug )
{
QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
alreadyPrintedDebug = true;
}
}
QgsVector3D point( x, y, z );
QgsVector3D p = context.map().mapToWorldCoordinates( point );

Expand Down Expand Up @@ -611,6 +653,8 @@ void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex
const QgsVector3D offset = pc->offset();
const double zValueScale = context.zValueScale();
const double zValueOffset = context.zValueFixedOffset();
QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
bool alreadyPrintedDebug = false;

QSet<int> filteredOutValues = context.getFilteredOutValues();
for ( int i = 0; i < count; ++i )
Expand All @@ -625,8 +669,19 @@ void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex
double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
double z = ( offset.z() + scale.z() * iz ) * zValueScale + zValueOffset;
try
{
coordinateTransform.transformInPlace( x, y, z );
}
catch ( QgsCsException &e )
{
if ( !alreadyPrintedDebug )
{
QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
alreadyPrintedDebug = true;
}
}
QgsVector3D point( x, y, z );

QgsVector3D p = context.map().mapToWorldCoordinates( point );
float iParam = 0.0f;
if ( attrIsX )
Expand Down
19 changes: 19 additions & 0 deletions src/core/qgscoordinatetransform.cpp
Expand Up @@ -24,6 +24,7 @@
#include "qgsexception.h"
#include "qgsproject.h"
#include "qgsreadwritelocker.h"
#include "qgsvector3d.h"

//qt includes
#include <QDomNode>
Expand Down Expand Up @@ -310,6 +311,24 @@ QgsRectangle QgsCoordinateTransform::transform( const QgsRectangle &rect, Transf
return QgsRectangle( x1, y1, x2, y2 );
}

QgsVector3D QgsCoordinateTransform::transform( const QgsVector3D &point, TransformDirection direction ) const
{
double x = point.x();
double y = point.y();
double z = point.z();
try
{
transformCoords( 1, &x, &y, &z, direction );
}
catch ( const QgsCsException & )
{
// rethrow the exception
QgsDebugMsgLevel( QStringLiteral( "rethrowing exception" ), 2 );
throw;
}
return QgsVector3D( x, y, z );
}

void QgsCoordinateTransform::transformInPlace( double &x, double &y, double &z,
TransformDirection direction ) const
{
Expand Down

0 comments on commit d80c27d

Please sign in to comment.