Skip to content
Permalink
Browse files

Address Nyall suggestions

  • Loading branch information
NEDJIMAbelgacem authored and nyalldawson committed Jan 4, 2021
1 parent 07512be commit 8178add788ea22736ae1c7612f5bfa19b657065d
@@ -56,6 +56,13 @@ Ctor

~QgsPointCloudDataProvider();

QVector<QMap<QString, QVariant>> identify( QgsPointCloudLayer *layer, float maximumError, float rootErrorPixels, QgsGeometry extentGeometry, const QgsDoubleRange extentZRange );
%Docstring
Returns the list of points of the point cloud layer ``layer`` according to a zoom level
defined by ``maximumError`` and ``rootErrorPixels``, and an extent defined by a geometry
in the 2D plane ``geometry`` and a range for z values ``extentZRange``
%End

virtual QgsPointCloudDataProvider::Capabilities capabilities() const;
%Docstring
Returns flags containing the supported capabilities for the data provider.
@@ -238,6 +245,7 @@ Returns the map of LAS data format ID to translated string value.
%Docstring
Emitted when point cloud generation state is changed
%End

};

/************************************************************************
@@ -201,6 +201,8 @@ and generate a proper subclass.
Renders a ``block`` of point cloud data using the specified render ``context``.
%End

QVector<QMap<QString, QVariant>> identify( QgsPointCloudLayer *layer, QgsRenderContext context, const QgsGeometry &geometry );

virtual bool willRenderPoint( const QMap<QString, QVariant> &pointAttributes );
%Docstring
Checks whether the point holding ``pointAttributes`` attributes will be rendered
@@ -139,7 +139,7 @@ bool QgsPointCloudClassifiedRenderer::willRenderPoint( const QMap<QString, QVari
if ( !pointAttributes.contains( mAttribute ) )
return false;
bool parsedCorrectly;
int attributeInt = pointAttributes[ mAttribute ].toReal( &parsedCorrectly );
int attributeInt = pointAttributes[ mAttribute ].toInt( &parsedCorrectly );
if ( !parsedCorrectly )
return false;
for ( const QgsPointCloudCategory &category : qgis::as_const( mCategories ) )
@@ -19,6 +19,11 @@
#include "qgspointclouddataprovider.h"
#include "qgspointcloudindex.h"
#include "qgsgeometry.h"
#include "qgspointcloudlayer.h"
#include "qgspointcloudlayerelevationproperties.h"
#include "qgspointcloudrequest.h"
#include "qgslogger.h"
#include "qgscircle.h"
#include <mutex>

QgsPointCloudDataProvider::QgsPointCloudDataProvider(
@@ -176,3 +181,94 @@ QVariant QgsPointCloudDataProvider::metadataClassStatistic( const QString &, con
{
return QVariant();
}

QVector<QMap<QString, QVariant>> QgsPointCloudDataProvider::identify(
QgsPointCloudLayer *layer,
float maximumError,
float rootErrorPixels,
QgsGeometry extentGeometry,
const QgsDoubleRange extentZRange )
{
QVector<QMap<QString, QVariant>> acceptedPoints;

QgsPointCloudIndex *index = this->index();
const IndexedPointCloudNode root = index->root();


QgsPointCloudLayerElevationProperties *properties = dynamic_cast<QgsPointCloudLayerElevationProperties *>( layer->elevationProperties() );
QgsRenderContext renderContext;

QVector<IndexedPointCloudNode> nodes = traverseTree( index, root, maximumError, rootErrorPixels, extentGeometry, extentZRange );

QgsPointCloudAttributeCollection attributeCollection = index->attributes();
QgsPointCloudRequest request;
request.setAttributes( attributeCollection );

QgsPointCloudRenderContext context( renderContext, index->scale(), index->offset(), properties->zScale(), properties->zOffset() );
int pointCount = 0;

for ( const IndexedPointCloudNode &n : nodes )
{
std::unique_ptr<QgsPointCloudBlock> block( index->nodeData( n, request ) );

if ( !block )
continue;

const char *ptr = block->data();
QgsPointCloudAttributeCollection blockAttributes = block->attributes();
const std::size_t recordSize = blockAttributes.pointRecordSize();
context.setAttributes( block->attributes() );
for ( int i = 0; i < block->pointCount(); ++i )
{
double x, y, z;
pointXY( context, ptr, i, x, y );
z = pointZ( context, ptr, i );
QgsPointXY pointXY( x, y );

if ( extentGeometry.contains( &pointXY ) && extentZRange.contains( z ) )
{
QMap<QString, QVariant> pointAttr = context.attributeMap( ptr, i * recordSize, blockAttributes );
pointAttr[ QStringLiteral( "X" ) ] = x;
pointAttr[ QStringLiteral( "Y" ) ] = y;
pointAttr[ QStringLiteral( "Z" ) ] = z;
acceptedPoints.push_back( pointAttr );
}
}
pointCount += block->pointCount();
}

return acceptedPoints;
}

QVector<IndexedPointCloudNode> QgsPointCloudDataProvider::traverseTree(
const QgsPointCloudIndex *pc,
IndexedPointCloudNode n,
float maxErrorPixels,
float nodeErrorPixels,
const QgsGeometry &extentGeometry,
const QgsDoubleRange extentZRange )
{
QVector<IndexedPointCloudNode> nodes;

const QgsDoubleRange nodeZRange = pc->nodeZRange( n );
if ( !extentZRange.overlaps( nodeZRange ) )
return nodes;

if ( !extentGeometry.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 )
{
if ( extentGeometry.intersects( pc->nodeMapExtent( nn ) ) )
nodes += traverseTree( pc, nn, maxErrorPixels, childrenErrorPixels, extentGeometry, extentZRange );
}

return nodes;
}
@@ -22,8 +22,12 @@
#include "qgsdataprovider.h"
#include "qgspointcloudattribute.h"
#include "qgsstatisticalsummary.h"
#include "qgspointcloudrenderer.h"
#include "qgsidentifycontext.h"
#include <memory>

class QgsPointCloudLayer;
class IndexedPointCloudNode;
class QgsPointCloudIndex;
class QgsPointCloudRenderer;
class QgsGeometry;
@@ -73,6 +77,13 @@ class CORE_EXPORT QgsPointCloudDataProvider: public QgsDataProvider

~QgsPointCloudDataProvider() override;

/**
* Returns the list of points of the point cloud layer \a layer according to a zoom level
* defined by \a maximumError and \a rootErrorPixels, and an extent defined by a geometry
* in the 2D plane \a geometry and a range for z values \a extentZRange
*/
QVector<QMap<QString, QVariant>> identify( QgsPointCloudLayer *layer, float maximumError, float rootErrorPixels, QgsGeometry extentGeometry, const QgsDoubleRange extentZRange );

/**
* Returns flags containing the supported capabilities for the data provider.
*/
@@ -293,6 +304,30 @@ class CORE_EXPORT QgsPointCloudDataProvider: public QgsDataProvider
* Emitted when point cloud generation state is changed
*/
void indexGenerationStateChanged( PointCloudIndexGenerationState state );

private:

/**
* Retrieves the x and y coordinate for the point at index \a i.
*/
static void pointXY( QgsPointCloudRenderContext &context, const char *ptr, int i, double &x, double &y )
{
const qint32 ix = *reinterpret_cast< const qint32 * >( ptr + i * context.pointRecordSize() + context.xOffset() );
const qint32 iy = *reinterpret_cast< const qint32 * >( ptr + i * context.pointRecordSize() + context.yOffset() );
x = context.offset().x() + context.scale().x() * ix;
y = context.offset().y() + context.scale().y() * iy;
}

/**
* Retrieves the z value for the point at index \a i.
*/
static double pointZ( QgsPointCloudRenderContext &context, const char *ptr, int i )
{
const qint32 iz = *reinterpret_cast<const qint32 * >( ptr + i * context.pointRecordSize() + context.zOffset() );
return context.offset().z() + context.scale().z() * iz;
}

QVector<IndexedPointCloudNode> traverseTree( const QgsPointCloudIndex *pc, IndexedPointCloudNode n, float maxErrorPixels, float nodeErrorPixels, const QgsGeometry &extentGeometry, const QgsDoubleRange extentZRange );
};

#endif // QGSMESHDATAPROVIDER_H
@@ -183,119 +183,6 @@ bool QgsPointCloudLayerRenderer::forceRasterRender() const
return mRenderer ? mRenderer->type() != QLatin1String( "extent" ) : false;
}

QVector<QMap<QString, QVariant>> QgsPointCloudLayerRenderer::identify( const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
{
QVector<QMap<QString, QVariant>> acceptedPoints;

QgsPointCloudIndex *index = mLayer->dataProvider()->index();
const IndexedPointCloudNode root = index->root();
QgsPointCloudRenderContext context( *renderContext(), index->scale(), index->offset() );

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

const QgsRectangle rootNodeExtentLayerCoords = index->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() / index->span(); // in map coords

double mapUnitsPerPixel = context.renderContext().mapToPixel().mapUnitsPerPixel();
if ( ( rootErrorInMapCoordinates < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumError < 0.0 ) )
{
QgsDebugMsg( QStringLiteral( "invalid screen error" ) );
return acceptedPoints;
}
float rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels

QVector<IndexedPointCloudNode> nodes = traverseTree( index, context.renderContext(), root, maximumError, rootErrorPixels, geometry );

QgsPointCloudAttributeCollection attributeCollection = mLayer->attributes();
QgsPointCloudRequest request;
request.setAttributes( attributeCollection );

// TODO: figure out why the reprojection here is invalid
// even thought we get a valid one in the renderer classes
const QgsCoordinateTransform ct = context.renderContext().coordinateTransform();
const bool reproject = ct.isValid();

for ( const IndexedPointCloudNode &n : nodes )
{
std::unique_ptr<QgsPointCloudBlock> block( index->nodeData( n, request ) );

if ( !block )
continue;

context.setAttributes( block->attributes() );

const char *ptr = block->data();
QgsPointCloudAttributeCollection blockAttributes = block->attributes();
const std::size_t recordSize = blockAttributes.pointRecordSize();
for ( int i = 0; i < block->pointCount(); ++i )
{
double x, y, z;
pointXY( context, ptr, i, x, y );
z = pointZ( context, ptr, i );
if ( reproject )
{
try
{
ct.transformInPlace( x, y, z );
}
catch ( QgsCsException & )
{
continue;
}
}
bool intersects = false;
// TODO: ask about this
// For some reason I need to multiply the pointSize by 2 to have correct point selection behaviour
// Inconvenience: whwn a point is not fully inside the selection area it gets selected too
if ( mRenderer->pointSymbol() == QgsPointCloudRenderer::PointSymbol::Square )
{
QgsPointXY deviceCoords = context.renderContext().mapToPixel().transform( QgsPointXY( x, y ) );
QgsPointXY point1( deviceCoords.x() - 2 * mRenderer->pointSize(), deviceCoords.y() - 2 * mRenderer->pointSize() );
QgsPointXY point2( deviceCoords.x() + 2 * mRenderer->pointSize(), deviceCoords.y() + 2 * mRenderer->pointSize() );
QgsPointXY point1MapCoords = context.renderContext().mapToPixel().toMapCoordinates( point1.x(), point1.y() );
QgsPointXY point2MapCoords = context.renderContext().mapToPixel().toMapCoordinates( point2.x(), point2.y() );
QgsRectangle pointRect( point1MapCoords, point2MapCoords );
intersects = geometry.intersects( pointRect );
}
else if ( mRenderer->pointSymbol() == QgsPointCloudRenderer::PointSymbol::Circle )
{
QgsPoint centerMapCoords( x, y );
QgsPointXY deviceCoords = context.renderContext().mapToPixel().transform( centerMapCoords );
QgsPoint point1( deviceCoords.x(), deviceCoords.y() - 2 * mRenderer->pointSize() );
QgsPoint point2( deviceCoords.x(), deviceCoords.y() + 2 * mRenderer->pointSize() );
QgsPointXY point1MapCoords = context.renderContext().mapToPixel().toMapCoordinates( point1.x(), point1.y() );
QgsPointXY point2MapCoords = context.renderContext().mapToPixel().toMapCoordinates( point2.x(), point2.y() );
QgsCircle circle = QgsCircle::from2Points( QgsPoint( point1MapCoords ), QgsPoint( point2MapCoords ) );
// TODO: make this faster?
QgsPolygon *polygon = circle.toPolygon( 5 );
QgsGeometry circleGeometry( polygon );
intersects = geometry.intersects( circleGeometry );
}
if ( intersects )
{
QMap<QString, QVariant> pointAttr = context.attributeMap( ptr, i * recordSize, blockAttributes );
pointAttr[ QStringLiteral( "X" ) ] = x;
pointAttr[ QStringLiteral( "Y" ) ] = y;
if ( mRenderer->willRenderPoint( pointAttr ) )
acceptedPoints.push_back( pointAttr );
}
}
}

return acceptedPoints;
}

QVector<IndexedPointCloudNode> QgsPointCloudLayerRenderer::traverseTree( const QgsPointCloudIndex *pc,
const QgsRenderContext &context,
IndexedPointCloudNode n,
@@ -61,7 +61,6 @@ class CORE_EXPORT QgsPointCloudLayerRenderer: public QgsMapLayerRenderer

bool render() override;
bool forceRasterRender() const override;
QVector<QMap<QString, QVariant>> identify( const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext );

private:

@@ -19,6 +19,10 @@
#include "qgspointcloudrendererregistry.h"
#include "qgsapplication.h"
#include "qgssymbollayerutils.h"
#include "qgspointcloudlayer.h"
#include "qgspointcloudindex.h"
#include "qgspointcloudlayerelevationproperties.h"
#include "qgslogger.h"

QgsPointCloudRenderContext::QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset, double zValueScale, double zValueFixedOffset )
: mRenderContext( context )
@@ -190,4 +194,49 @@ void QgsPointCloudRenderer::setPointSymbol( PointSymbol symbol )
mPointSymbol = symbol;
}

QVector<QMap<QString, QVariant>> QgsPointCloudRenderer::identify( QgsPointCloudLayer *layer, QgsRenderContext renderContext, const QgsGeometry &geometry )
{
QVector<QMap<QString, QVariant>> selectedPoints;

QgsPointCloudIndex *index = layer->dataProvider()->index();
const IndexedPointCloudNode root = index->root();
QgsPointCloudLayerElevationProperties *properties = dynamic_cast<QgsPointCloudLayerElevationProperties *>( layer->elevationProperties() );
QgsPointCloudRenderContext context( renderContext, index->scale(), index->offset(), properties->zScale(), properties->zOffset() );

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

const QgsRectangle rootNodeExtentLayerCoords = index->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() / index->span(); // in map coords

double mapUnitsPerPixel = context.renderContext().mapToPixel().mapUnitsPerPixel();
if ( ( rootErrorInMapCoordinates < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumError < 0.0 ) )
{
QgsDebugMsg( QStringLiteral( "invalid screen error" ) );
return selectedPoints;
}

float rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels

QVector<QMap<QString, QVariant>> points = layer->dataProvider()->identify( layer, maximumError, rootErrorPixels, geometry, QgsDoubleRange() );

for ( QMap<QString, QVariant> point : points )
{
if ( this->willRenderPoint( point ) )
selectedPoints.push_back( point );
}

return selectedPoints;
}


0 comments on commit 8178add

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