Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor QgsPointCloudIndex #59564

Merged
merged 9 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -170,80 +170,6 @@ Only providers which report the CreateRenderer capability will return a 2D rende
providers will return ``None``.
%End

virtual bool hasStatisticsMetadata() const;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all an API break -- we need to keep all methods exposed to Python for the lifetime of 3.x

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nyalldawson QgsPointCloudDataProvider has been intentionally marked in docstring as experimental, allowing changes at in future - so I believe we should allow API breaks here...

%Docstring
Returns whether the dataset contains statistics metadata

.. versionadded:: 3.26
%End


SIP_PYOBJECT metadataStatistic( const QString &attribute, Qgis::Statistic statistic ) const;
%Docstring
Returns a statistic for the specified ``attribute``, taken only from the metadata of the point cloud
data source.

This method will not perform any statistical calculations, rather it will return only precomputed attribute
statistics which are included in the data source's metadata. Not all data sources include this information
in the metadata, and even for sources with statistical metadata only some ``statistic`` values may be available.

:raises ValueError: if no matching precalculated statistic is available for the attribute.
%End
%MethodCode
{
const QVariant res = sipCpp->metadataStatistic( *a0, a1 );
if ( !res.isValid() )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Statistic is not available" ).toUtf8().constData() );
sipIsErr = 1;
}
else
{
QVariant *v = new QVariant( res );
sipRes = sipConvertFromNewType( v, sipType_QVariant, Py_None );
}
}
%End

virtual QVariantList metadataClasses( const QString &attribute ) const;
%Docstring
Returns a list of existing classes which are present for the specified ``attribute``, taken only from the
metadata of the point cloud data source.

This method will not perform any classification or scan for available classes, rather it will return only
precomputed classes which are included in the data source's metadata. Not all data sources include this information
in the metadata.
%End



SIP_PYOBJECT metadataClassStatistic( const QString &attribute, const QVariant &value, Qgis::Statistic statistic ) const;
%Docstring
Returns a statistic for one class ``value`` from the specified ``attribute``, taken only from the metadata of the point cloud
data source.
This method will not perform any statistical calculations, rather it will return only precomputed class
statistics which are included in the data source's metadata. Not all data sources include this information
in the metadata, and even for sources with statistical metadata only some ``statistic`` values may be available.

:raises ValueError: if no matching precalculated statistic is available for the attribute.
%End
%MethodCode
{
const QVariant res = sipCpp->metadataClassStatistic( *a0, *a1, a2 );
if ( !res.isValid() )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Statistic is not available" ).toUtf8().constData() );
sipIsErr = 1;
}
else
{
QVariant *v = new QVariant( res );
sipRes = sipConvertFromNewType( v, sipType_QVariant, Py_None );
}
}
%End


QgsPointCloudStatistics metadataStatistics();
%Docstring
Returns the object containing the statistics metadata extracted from the dataset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ struct QgsPointCloudAttributeStatistics
double mean;
double stDev;
int count;

int singleClassCount( int cls ) const;
%Docstring
Returns the count of points in given class or -1 on error

.. versionadded:: 3.42
%End
};

class QgsPointCloudStatistics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,80 +170,6 @@ Only providers which report the CreateRenderer capability will return a 2D rende
providers will return ``None``.
%End

virtual bool hasStatisticsMetadata() const;
%Docstring
Returns whether the dataset contains statistics metadata

.. versionadded:: 3.26
%End


SIP_PYOBJECT metadataStatistic( const QString &attribute, Qgis::Statistic statistic ) const;
%Docstring
Returns a statistic for the specified ``attribute``, taken only from the metadata of the point cloud
data source.

This method will not perform any statistical calculations, rather it will return only precomputed attribute
statistics which are included in the data source's metadata. Not all data sources include this information
in the metadata, and even for sources with statistical metadata only some ``statistic`` values may be available.

:raises ValueError: if no matching precalculated statistic is available for the attribute.
%End
%MethodCode
{
const QVariant res = sipCpp->metadataStatistic( *a0, a1 );
if ( !res.isValid() )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Statistic is not available" ).toUtf8().constData() );
sipIsErr = 1;
}
else
{
QVariant *v = new QVariant( res );
sipRes = sipConvertFromNewType( v, sipType_QVariant, Py_None );
}
}
%End

virtual QVariantList metadataClasses( const QString &attribute ) const;
%Docstring
Returns a list of existing classes which are present for the specified ``attribute``, taken only from the
metadata of the point cloud data source.

This method will not perform any classification or scan for available classes, rather it will return only
precomputed classes which are included in the data source's metadata. Not all data sources include this information
in the metadata.
%End



SIP_PYOBJECT metadataClassStatistic( const QString &attribute, const QVariant &value, Qgis::Statistic statistic ) const;
%Docstring
Returns a statistic for one class ``value`` from the specified ``attribute``, taken only from the metadata of the point cloud
data source.
This method will not perform any statistical calculations, rather it will return only precomputed class
statistics which are included in the data source's metadata. Not all data sources include this information
in the metadata, and even for sources with statistical metadata only some ``statistic`` values may be available.

:raises ValueError: if no matching precalculated statistic is available for the attribute.
%End
%MethodCode
{
const QVariant res = sipCpp->metadataClassStatistic( *a0, *a1, a2 );
if ( !res.isValid() )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Statistic is not available" ).toUtf8().constData() );
sipIsErr = 1;
}
else
{
QVariant *v = new QVariant( res );
sipRes = sipConvertFromNewType( v, sipType_QVariant, Py_None );
}
}
%End


QgsPointCloudStatistics metadataStatistics();
%Docstring
Returns the object containing the statistics metadata extracted from the dataset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ struct QgsPointCloudAttributeStatistics
double mean;
double stDev;
int count;

int singleClassCount( int cls ) const;
%Docstring
Returns the count of points in given class or -1 on error

.. versionadded:: 3.42
%End
};

class QgsPointCloudStatistics
Expand Down
45 changes: 24 additions & 21 deletions src/3d/qgspointcloudlayerchunkloader_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "moc_qgspointcloudlayerchunkloader_p.cpp"

#include "qgs3dutils.h"
#include "qgsbox3d.h"
#include "qgspointcloudlayer3drenderer.h"
#include "qgschunknode.h"
#include "qgslogger.h"
Expand Down Expand Up @@ -57,7 +58,7 @@ QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointClou
mContext.setAttributes( pc->attributes() );

const QgsChunkNodeId nodeId = node->tileId();
const IndexedPointCloudNode pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );

Q_ASSERT( pc->hasNode( pcNode ) );

Expand Down Expand Up @@ -128,7 +129,7 @@ Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntit
{
QgsPointCloudIndex *pc = mFactory->mPointCloudIndex;
const QgsChunkNodeId nodeId = mNode->tileId();
const IndexedPointCloudNode pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
Q_ASSERT( pc->hasNode( pcNode ) );

Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
Expand Down Expand Up @@ -165,28 +166,28 @@ QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChun
{
const QgsChunkNodeId id = node->tileId();

Q_ASSERT( mPointCloudIndex->hasNode( IndexedPointCloudNode( id.d, id.x, id.y, id.z ) ) );
Q_ASSERT( mPointCloudIndex->hasNode( QgsPointCloudNodeId( 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
{
const QgsChunkNodeId id = node->tileId();
const IndexedPointCloudNode n( id.d, id.x, id.y, id.z );
const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
Q_ASSERT( mPointCloudIndex->hasNode( n ) );
return mPointCloudIndex->nodePointCount( n );
return mPointCloudIndex->getNode( n ).pointCount();
}


QgsBox3D nodeBoundsToBox3D( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const QgsCoordinateTransform &coordinateTransform, double zValueOffset, double zValueScale )
static QgsBox3D nodeBoundsToBox3D( QgsBox3D nodeBounds, const QgsCoordinateTransform &coordinateTransform, double zValueOffset, double zValueScale )
{
QgsVector3D extentMin3D( static_cast<double>( nodeBounds.xMin() ) * scale.x() + offset.x(),
static_cast<double>( nodeBounds.yMin() ) * scale.y() + offset.y(),
( static_cast<double>( nodeBounds.zMin() ) * scale.z() + offset.z() ) * zValueScale + zValueOffset );
QgsVector3D extentMax3D( static_cast<double>( nodeBounds.xMax() ) * scale.x() + offset.x(),
static_cast<double>( nodeBounds.yMax() ) * scale.y() + offset.y(),
( static_cast<double>( nodeBounds.zMax() ) * scale.z() + offset.z() ) * zValueScale + zValueOffset );
QgsVector3D extentMin3D( nodeBounds.xMinimum(),
nodeBounds.yMinimum(),
nodeBounds.zMinimum() * zValueScale + zValueOffset );
QgsVector3D extentMax3D( nodeBounds.xMaximum(),
nodeBounds.yMaximum(),
nodeBounds.zMaximum() * zValueScale + zValueOffset );
QgsCoordinateTransform extentTransform = coordinateTransform;
extentTransform.setBallparkTransformsAreAppropriate( true );
try
Expand All @@ -205,10 +206,11 @@ QgsBox3D nodeBoundsToBox3D( QgsPointCloudDataBounds nodeBounds, QgsVector3D offs

QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode() const
{
const QgsPointCloudDataBounds rootNodeBounds = mPointCloudIndex->nodeBounds( IndexedPointCloudNode( 0, 0, 0, 0 ) );
QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mPointCloudIndex->offset(), mPointCloudIndex->scale(), mCoordinateTransform, mZValueOffset, mZValueScale );
const QgsPointCloudNode pcNode = mPointCloudIndex->getNode( mPointCloudIndex->root() );
const QgsBox3D rootNodeBounds = pcNode.bounds();
QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );

const float error = mPointCloudIndex->nodeError( IndexedPointCloudNode( 0, 0, 0, 0 ) );
const float error = pcNode.error();
QgsChunkNode *node = new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
node->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
return node;
Expand All @@ -224,15 +226,16 @@ QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( Qg
{
int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );

if ( !mPointCloudIndex->hasNode( IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
const QgsPointCloudNodeId childPcId( childId.d, childId.x, childId.y, childId.z );
if ( !mPointCloudIndex->hasNode( childPcId ) )
continue;
const QgsPointCloudNode childNode = mPointCloudIndex->getNode( childPcId );
const QgsBox3D childBounds = childNode.bounds();
if ( !mExtent.isEmpty() &&
!mPointCloudIndex->nodeMapExtent( IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ).intersects( mExtent ) )
!childBounds.intersects( mExtent ) )
continue;

const QgsPointCloudDataBounds childBounds = mPointCloudIndex->nodeBounds( IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) );
QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mPointCloudIndex->offset(), mPointCloudIndex->scale(), mCoordinateTransform, mZValueOffset, mZValueScale );
QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );

QgsChunkNode *child = new QgsChunkNode( childId, childBox3D, childError, node );
child->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
Expand Down Expand Up @@ -302,7 +305,7 @@ QVector<QgsRayCastingUtils::RayHit> QgsPointCloudLayerChunkedEntity::rayIntersec
for ( QgsChunkNode *node : activeNodes )
{
const QgsChunkNodeId id = node->tileId();
const IndexedPointCloudNode n( id.d, id.x, id.y, id.z );
const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );

if ( !index->hasNode( n ) )
continue;
Expand Down
Loading
Loading