Skip to content
Permalink
Browse files

traverse tree in 2D rendering by map error

  • Loading branch information
PeterPetrik authored and wonder-sk committed Nov 5, 2020
1 parent 40c9eb6 commit e1f1af7844685228bfce74dee4289e6cb62baa3e
@@ -150,25 +150,7 @@ QgsPointCloudIndex::QgsPointCloudIndex() = default;

QgsPointCloudIndex::~QgsPointCloudIndex() = default;

QList<IndexedPointCloudNode> QgsPointCloudIndex::traverseTree( const QgsRectangle &extent, IndexedPointCloudNode n, int maxDepth )
{
QList<IndexedPointCloudNode> nodes;

if ( !extent.intersects( nodeMapExtent( n ) ) )
return nodes;

nodes.append( n );

for ( auto nn : children( n ) )
{
if ( maxDepth > 0 )
nodes += traverseTree( extent, nn, maxDepth - 1 );
}

return nodes;
}

QList<IndexedPointCloudNode> QgsPointCloudIndex::children( const IndexedPointCloudNode &n )
QList<IndexedPointCloudNode> QgsPointCloudIndex::nodeChildren( const IndexedPointCloudNode &n ) const
{
Q_ASSERT( mHierarchy.contains( n ) );
QList<IndexedPointCloudNode> lst;
@@ -153,11 +153,8 @@ class CORE_EXPORT QgsPointCloudIndex: public QObject
//! Returns whether the octree contain given node
bool hasNode( const IndexedPointCloudNode &n ) const { return mHierarchy.contains( n ); }

//! Traverses tree and returns all nodes in specified depth
QList<IndexedPointCloudNode> traverseTree( const QgsRectangle &extent, IndexedPointCloudNode n, int maxDepth = 3 );

//! Returns all children of node
QList<IndexedPointCloudNode> children( const IndexedPointCloudNode &n );
QList<IndexedPointCloudNode> nodeChildren( const IndexedPointCloudNode &n ) const;

//! Returns all attributes that are stored in the file
QgsPointCloudAttributeCollection attributes() const;
@@ -87,26 +87,28 @@ void QgsPointCloudRendererConfig::setColorRamp( const QgsColorRamp *value )
mColorRamp.reset( value->clone() );
}

float QgsPointCloudRendererConfig::maximumScreenError() const
{
return mMaximumScreenError;
}

///@endcond

QgsPointCloudLayerRenderer::QgsPointCloudLayerRenderer( QgsPointCloudLayer *layer, QgsRenderContext &context )
: QgsMapLayerRenderer( layer->id(), &context )
, mLayer( layer )
{
// TODO: we must not keep pointer to mLayer (it's dangerous) - we must copy anything we need for rendering
// or use some locking to prevent read/write from multiple threads

// TODO: use config from layer
mConfig.setPenWidth( context.convertToPainterUnits( 1, QgsUnitTypes::RenderUnit::RenderMillimeters ) );
// good range for 26850_12580.laz
mConfig.setZMin( layer->customProperty( QStringLiteral( "pcMin" ), 400 ).toInt() );
mConfig.setZMax( layer->customProperty( QStringLiteral( "pcMax" ), 600 ).toInt() );
mConfig.setColorRamp( QgsStyle::defaultStyle()->colorRamp( layer->customProperty( QStringLiteral( "pcRamp" ), QStringLiteral( "Viridis" ) ).toString() ) );
}

static QList<IndexedPointCloudNode> _traverseTree( QgsPointCloudIndex *pc, const QgsRectangle &extent, IndexedPointCloudNode n, int maxDepth )
{
return pc->traverseTree( extent, n, maxDepth );
// TODO: we must not keep pointer to mLayer (it's dangerous) - we must copy anything we need for rendering
// or use some locking to prevent read/write from multiple threads
if ( !mLayer || !mLayer->dataProvider() || !mLayer->dataProvider()->index() )
return;
}

bool QgsPointCloudLayerRenderer::render()
@@ -129,12 +131,20 @@ bool QgsPointCloudLayerRenderer::render()
QElapsedTimer t;
t.start();

// TODO: set depth based on map units per pixel
int depth = 3;
QList<IndexedPointCloudNode> nodes = _traverseTree( pc, context.mapExtent(), pc->root(), depth );
const IndexedPointCloudNode root = pc->root();
float maximumError = mConfig.maximumScreenError(); // in pixels
float rootError = pc->nodeError( root ); // in map coords
double mapUnitsPerPixel = context.mapToPixel().mapUnitsPerPixel();
if ( ( rootError < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumError < 0.0 ) )
{
qDebug() << "invalid screen error";
return false;
}
float rootErrorPixels = rootError / mapUnitsPerPixel; // in pixels
const QList<IndexedPointCloudNode> nodes = traverseTree( pc, context, pc->root(), maximumError, rootErrorPixels );

// drawing
for ( auto n : nodes )
for ( const IndexedPointCloudNode &n : nodes )
{
if ( context.renderingStopped() )
{
@@ -159,6 +169,37 @@ bool QgsPointCloudLayerRenderer::render()
return true;
}

QList<IndexedPointCloudNode> QgsPointCloudLayerRenderer::traverseTree( const QgsPointCloudIndex *pc,
const QgsRenderContext &context,
IndexedPointCloudNode n,
float maxErrorPixels,
float nodeErrorPixels )
{
QList<IndexedPointCloudNode> nodes;

if ( context.renderingStopped() )
{
qDebug() << "canceled";
return nodes;
}

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

nodes.append( n );

float childrenErrorPixels = nodeErrorPixels / 2.0f;
if ( childrenErrorPixels < maxErrorPixels )
return nodes;

const QList<IndexedPointCloudNode> children = pc->nodeChildren( n );
for ( const IndexedPointCloudNode &nn : children )
{
nodes += traverseTree( pc, context, nn, maxErrorPixels, childrenErrorPixels );
}

return nodes;
}

QgsPointCloudLayerRenderer::~QgsPointCloudLayerRenderer() = default;

@@ -71,10 +71,14 @@ class CORE_EXPORT QgsPointCloudRendererConfig
//! Sets color ramp
void setColorRamp( const QgsColorRamp *value );

//! Returns maximum allowed screen error in pixels
float maximumScreenError() const;

private:
double mZMin = 0, mZMax = 0;
int mPenWidth = 1;
std::unique_ptr<QgsColorRamp> mColorRamp;
float mMaximumScreenError = 5;
};

///@endcond
@@ -100,6 +104,10 @@ class CORE_EXPORT QgsPointCloudLayerRenderer: public QgsMapLayerRenderer
bool render() override;

private:

//! Traverses tree and returns all nodes in specified depth
QList<IndexedPointCloudNode> traverseTree( const QgsPointCloudIndex *pc, const QgsRenderContext &context, IndexedPointCloudNode n, float maxErrorPixels, float nodeErrorPixels );

QgsPointCloudLayer *mLayer = nullptr;

QgsPointCloudRendererConfig mConfig;

0 comments on commit e1f1af7

Please sign in to comment.
You can’t perform that action at this time.