Skip to content

Commit

Permalink
move the implementation to Qgs3DMapToolIdentify
Browse files Browse the repository at this point in the history
  • Loading branch information
NEDJIMAbelgacem authored and wonder-sk committed Jan 13, 2021
1 parent be88fff commit 05735cd
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 232 deletions.
Expand Up @@ -89,42 +89,6 @@ the number of points returned to ``pointsLimit`` points
}
%End


SIP_PYLIST getPointsOnRay( const QgsRay3D &ray, double maxScreenError, double cameraFov, int screenSizePx, double pointAngle, int pointsLimit = 1000 );
%Docstring
Returns the points that are on a ray

:param rayOrigin: : The origin of the ray in layer coordinates
:param rayDirection: : The direction of the ray in layer coordinates
:param maxScreenError: : Maximum screen error (as taken from the 3D point cloud layer renderer)
:param cameraFov: : The field of view of the camera in degrees
:param screenSizePx: : The size of the screen's viewport in pixels
:param pointAngle: : the maximum accepted angle between the point and it's projected point on the ray in degrees
:param pointsLimit: : the maximum number of points returned

:return: a list of the identified points

.. versionadded:: 3.18
%End
%MethodCode
{
QVector<QMap<QString, QVariant>> res = sipCpp->getPointsOnRay( *a0, a1, a2, a3, a4, a5 );
sipRes = PyList_New( res.size() );
for ( int i = 0; i < res.size(); ++i )
{
PyObject *dict = PyDict_New();
for ( QString key : res[i].keys() )
{
PyObject *keyObj = sipConvertFromNewType( new QString( key ), sipType_QString, Py_None );
PyObject *valObj = sipConvertFromNewType( new QVariant( res[i][key] ), sipType_QVariant, Py_None );
PyDict_SetItem( dict, keyObj, valObj );
}
PyList_SET_ITEM( sipRes, i, dict );
}
}
%End


virtual QgsPointCloudDataProvider::Capabilities capabilities() const;
%Docstring
Returns flags containing the supported capabilities for the data provider.
Expand Down
46 changes: 8 additions & 38 deletions src/3d/qgs3dmapscene.cpp
Expand Up @@ -80,6 +80,7 @@
#include "qgswindow3dengine.h"
#include "qgspointcloudlayerelevationproperties.h"
#include "qgspointcloudlayer.h"
#include "qgspointcloudlayerchunkloader_p.h"

Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *engine )
: mMap( map )
Expand Down Expand Up @@ -1102,45 +1103,14 @@ void Qgs3DMapScene::exportScene( const Qgs3DMapExportSettings &exportSettings )
}
}

void Qgs3DMapScene::identifyPointCloudOnRay( QVector<QPair<QgsMapLayer *, QVector<QVariantMap>>> &selectedPoints, const QgsRay3D &ray )
QVector<const QgsChunkNode *> Qgs3DMapScene::getLayerActiveChunkNodes( QgsMapLayer *layer )
{
QgsVector3D originMapCoords = mMap.worldToMapCoordinates( ray.origin() );
QgsVector3D pointMapCoords = mMap.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
QgsVector3D directionMapCoords = pointMapCoords - originMapCoords;
directionMapCoords.normalize();

QVector3D rayOriginMapCoords( originMapCoords.x(), originMapCoords.y(), originMapCoords.z() );
QVector3D rayDirectionMapCoords( directionMapCoords.x(), directionMapCoords.y(), directionMapCoords.z() );

QRect rect = mCameraController->viewport();
int screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct? (see _sceneState)
double fov = mCameraController->camera()->fieldOfView();

for ( QgsMapLayer *layer : mMap.layers() )
QVector<const QgsChunkNode *> chunks;
if ( !mLayerEntities.contains( layer ) ) return chunks;
if ( QgsChunkedEntity *c = qobject_cast<QgsChunkedEntity *>( mLayerEntities[ layer ] ) )
{
if ( layer->type() != QgsMapLayerType::PointCloudLayer )
continue;
if ( QgsPointCloudLayer *pc = qobject_cast<QgsPointCloudLayer *>( layer ) )
{
QgsPointCloudLayer3DRenderer *renderer = dynamic_cast<QgsPointCloudLayer3DRenderer *>( pc->renderer3D() );
const QgsPointCloud3DSymbol *symbol = renderer->symbol();
// Symbol can be null in case of no rendering enabled
if ( !symbol )
continue;
double maxScreenError = renderer->maximumScreenError();
double pointSize = symbol->pointSize();
double angle = pointSize / screenSizePx * mCameraController->camera()->fieldOfView();

// adjust ray to elevation properties
QgsPointCloudLayerElevationProperties *elevationProps = dynamic_cast<QgsPointCloudLayerElevationProperties *>( pc->elevationProperties() );
QVector3D adjutedRayOrigin = QVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - elevationProps->zOffset() ) / elevationProps->zScale() );
QVector3D adjutedRayDirection = QVector3D( rayDirectionMapCoords.x(), rayDirectionMapCoords.y(), rayDirectionMapCoords.z() / elevationProps->zScale() );
adjutedRayDirection.normalize();

QgsRay3D ray( adjutedRayOrigin, adjutedRayDirection );

QVector<QVariantMap> points = pc->dataProvider()->getPointsOnRay( ray, maxScreenError, fov, screenSizePx, angle );
selectedPoints.append( qMakePair( layer, points ) );
}
for ( QgsChunkNode *n : c->activeNodes() )
chunks.push_back( n );
}
return chunks;
}
7 changes: 3 additions & 4 deletions src/3d/qgs3dmapscene.h
Expand Up @@ -56,7 +56,7 @@ class QgsSkyboxSettings;
class Qgs3DMapExportSettings;
class QgsShadowRenderingFrameGraph;
class QgsPostprocessingEntity;

class QgsChunkNode;

#define SIP_NO_FILE

Expand Down Expand Up @@ -115,12 +115,11 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
void exportScene( const Qgs3DMapExportSettings &exportSettings );

/**
* Identifies the points that are positioned on the ray specified in world coordinates as a point \a rayOrigin and direction \a rayDirection
* for every point cloud layer and returns the identified points in \a selectedPoints vector
* Returns the active chunk nodes of \a layer
*
* \since QGIS 3.18
*/
void identifyPointCloudOnRay( QVector<QPair<QgsMapLayer *, QVector<QVariantMap>>> &selectedPoints, const QgsRay3D &ray );
QVector<const QgsChunkNode *> getLayerActiveChunkNodes( QgsMapLayer *layer ) SIP_SKIP;

signals:
//! Emitted when the current terrain entity is replaced by a new one
Expand Down
209 changes: 208 additions & 1 deletion src/app/3d/qgs3dmaptoolidentify.cpp
Expand Up @@ -32,6 +32,9 @@

#include "qgspointcloudlayer.h"
#include "qgspointcloudlayerelevationproperties.h"
#include "qgspointcloudattribute.h"
#include "qgspointcloudrequest.h"
#include "qgspointcloudlayer3drenderer.h"

#include "qgs3dmapscenepickhandler.h"
#include "qgs3dutils.h"
Expand Down Expand Up @@ -83,6 +86,117 @@ void Qgs3DMapToolIdentify::mousePressEvent( QMouseEvent *event )
identifyTool2D->clearResults();
}

template <typename T>
void _attribute( const char *data, std::size_t offset, QgsPointCloudAttribute::DataType type, T &value )
{
switch ( type )
{
case QgsPointCloudAttribute::Char:
value = *( data + offset );
break;

case QgsPointCloudAttribute::Int32:
value = *reinterpret_cast< const qint32 * >( data + offset );
break;

case QgsPointCloudAttribute::Short:
{
value = *reinterpret_cast< const short * >( data + offset );
}
break;

case QgsPointCloudAttribute::UShort:
value = *reinterpret_cast< const unsigned short * >( data + offset );
break;

case QgsPointCloudAttribute::Float:
value = static_cast< T >( *reinterpret_cast< const float * >( data + offset ) );
break;

case QgsPointCloudAttribute::Double:
value = *reinterpret_cast< const double * >( data + offset );
break;
}
}

/**
* Retrieves the x, y, z values for the point at index \a i.
*/
static void _pointXYZ( const char *ptr, int i, std::size_t pointRecordSize, int xOffset, QgsPointCloudAttribute::DataType xType,
int yOffset, QgsPointCloudAttribute::DataType yType,
int zOffset, QgsPointCloudAttribute::DataType zType,
const QgsVector3D &indexScale, const QgsVector3D &indexOffset, double &x, double &y, double &z )
{
_attribute( ptr, i * pointRecordSize + xOffset, xType, x );
x = indexOffset.x() + indexScale.x() * x;

_attribute( ptr, i * pointRecordSize + yOffset, yType, y );
y = indexOffset.y() + indexScale.y() * y;

_attribute( ptr, i * pointRecordSize + zOffset, zType, z );
z = indexOffset.z() + indexScale.z() * z;
}

/**
* Retrieves all the attributes of a point
*/
QVariantMap _attributeMap( const char *data, std::size_t recordOffset, const QgsPointCloudAttributeCollection &attributeCollection )
{
QVariantMap map;
const QVector<QgsPointCloudAttribute> attributes = attributeCollection.attributes();
for ( const QgsPointCloudAttribute &attr : attributes )
{
QString attributeName = attr.name();
int attributeOffset;
attributeCollection.find( attributeName, attributeOffset );
switch ( attr.type() )
{
case QgsPointCloudAttribute::Char:
{
const char value = *( data + recordOffset + attributeOffset );
map[ attributeName ] = value;
}
break;

case QgsPointCloudAttribute::Int32:
{
const qint32 value = *reinterpret_cast< const qint32 * >( data + recordOffset + attributeOffset );
map[ attributeName ] = value;
}
break;

case QgsPointCloudAttribute::Short:
{
const short value = *reinterpret_cast< const short * >( data + recordOffset + attributeOffset );
map[ attributeName ] = value;
}
break;

case QgsPointCloudAttribute::UShort:
{
const unsigned short value = *reinterpret_cast< const unsigned short * >( data + recordOffset + attributeOffset );
map[ attributeName ] = value;
}
break;

case QgsPointCloudAttribute::Float:
{
const float value = *reinterpret_cast< const float * >( data + recordOffset + attributeOffset );
map[ attributeName ] = value;
}
break;

case QgsPointCloudAttribute::Double:
{
const double value = *reinterpret_cast< const double * >( data + recordOffset + attributeOffset );
map[ attributeName ] = value;
}
break;
}
}
return map;
}

void Qgs3DMapToolIdentify::mouseReleaseEvent( QMouseEvent *event )
{
if ( event->button() != Qt::MouseButton::LeftButton )
Expand All @@ -93,7 +207,100 @@ void Qgs3DMapToolIdentify::mouseReleaseEvent( QMouseEvent *event )
Qgs3DMapCanvas *canvas = this->canvas();

QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( event->pos(), canvas->windowSize(), canvas->cameraController()->camera() );
canvas->scene()->identifyPointCloudOnRay( layerPoints, ray );

QMap<QgsPointCloudLayer *, QVector<IndexedPointCloudNode>> layerChunks;
for ( QgsMapLayer *layer : canvas->map()->layers() )
{
if ( QgsPointCloudLayer *pc = qobject_cast<QgsPointCloudLayer *>( layer ) )
{
QVector<IndexedPointCloudNode> pointCloudNodes;
for ( const QgsChunkNode *n : canvas->scene()->getLayerActiveChunkNodes( pc ) )
{
QgsChunkNodeId id = n->tileId();
pointCloudNodes.push_back( IndexedPointCloudNode( id.d, id.x, id.y, id.z ) );
}
if ( pointCloudNodes.empty() )
continue;
layerChunks[ pc ] = pointCloudNodes;
}
}

for ( QgsPointCloudLayer *layer : layerChunks.keys() )
{
// transform ray
QgsVector3D originMapCoords = canvas->map()->worldToMapCoordinates( ray.origin() );
QgsVector3D pointMapCoords = canvas->map()->worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
QgsVector3D directionMapCoords = pointMapCoords - originMapCoords;
directionMapCoords.normalize();

QVector3D rayOriginMapCoords( originMapCoords.x(), originMapCoords.y(), originMapCoords.z() );
QVector3D rayDirectionMapCoords( directionMapCoords.x(), directionMapCoords.y(), directionMapCoords.z() );

QRect rect = canvas->cameraController()->viewport();
int screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct? (see _sceneState)
QgsPointCloudLayer3DRenderer *renderer = dynamic_cast<QgsPointCloudLayer3DRenderer *>( layer->renderer3D() );
const QgsPointCloud3DSymbol *symbol = renderer->symbol();
// Symbol can be null in case of no rendering enabled
if ( !symbol )
continue;
double pointSize = symbol->pointSize();
double limitAngle = pointSize / screenSizePx * canvas->cameraController()->camera()->fieldOfView();

// adjust ray to elevation properties
QgsPointCloudLayerElevationProperties *elevationProps = dynamic_cast<QgsPointCloudLayerElevationProperties *>( layer->elevationProperties() );
QVector3D adjutedRayOrigin = QVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - elevationProps->zOffset() ) / elevationProps->zScale() );
QVector3D adjutedRayDirection = QVector3D( rayDirectionMapCoords.x(), rayDirectionMapCoords.y(), rayDirectionMapCoords.z() / elevationProps->zScale() );
adjutedRayDirection.normalize();

QgsRay3D layerRay( adjutedRayOrigin, adjutedRayDirection );

QgsPointCloudDataProvider *provider = layer->dataProvider();
QgsPointCloudIndex *index = provider->index();
QVector<QVariantMap> points;
QgsPointCloudAttributeCollection attributeCollection = index->attributes();
QgsPointCloudRequest request;
request.setAttributes( attributeCollection );
for ( IndexedPointCloudNode &n : layerChunks[layer] )
{
if ( !index->hasNode( n ) )
continue;
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();
int xOffset = 0, yOffset = 0, zOffset = 0;
const QgsPointCloudAttribute::DataType xType = blockAttributes.find( QStringLiteral( "X" ), xOffset )->type();
const QgsPointCloudAttribute::DataType yType = blockAttributes.find( QStringLiteral( "Y" ), yOffset )->type();
const QgsPointCloudAttribute::DataType zType = blockAttributes.find( QStringLiteral( "Z" ), zOffset )->type();
for ( int i = 0; i < block->pointCount(); ++i )
{
double x, y, z;
_pointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, index->scale(), index->offset(), x, y, z );
QVector3D point( x, y, z );

// check whether point is in front of the ray
if ( !layerRay.isInFront( point ) )
continue;

// calculate the angle between the point and the projected point
if ( layerRay.angleToPoint( point ) > limitAngle )
continue;

// Note : applying elevation properties is done in fromPointCloudIdentificationToIdentifyResults
QVariantMap pointAttr = _attributeMap( ptr, i * recordSize, blockAttributes );
pointAttr[ QStringLiteral( "X" ) ] = x;
pointAttr[ QStringLiteral( "Y" ) ] = y;
pointAttr[ QStringLiteral( "Z" ) ] = z;
pointAttr[ tr( "Distance to camera" ) ] = ( point - layerRay.origin() ).length();
points.push_back( pointAttr );
}

}
layerPoints.push_back( qMakePair( layer, points ) );
}

QList<QgsMapToolIdentify::IdentifyResult> identifyResults;
for ( int i = 0; i < layerPoints.size(); ++i )
Expand Down
12 changes: 6 additions & 6 deletions src/core/geometry/qgsray3d.cpp
Expand Up @@ -37,7 +37,7 @@ void QgsRay3D::setDirection( const QVector3D direction )

bool QgsRay3D::operator==( const QgsRay3D &r )
{
return this->mOrigin == r.origin() && this->direction() == r.direction();
return this->mOrigin == r.origin() && this->mDirection == r.direction();
}

QVector3D QgsRay3D::projectedPoint( const QVector3D &point ) const
Expand Down Expand Up @@ -89,16 +89,16 @@ bool QgsRay3D::intersectsWith( const QgsBox3d &box ) const

bool QgsRay3D::isInFront( const QVector3D &point ) const
{
return QVector3D::dotProduct( point - mOrigin, mDirection ) >= 0.0;
return QVector3D::dotProduct( ( point - mOrigin ).normalized(), mDirection ) >= 0.0;
}

double QgsRay3D::angleToPoint( const QVector3D &point ) const
{
// project point to the ray
QVector3D projectedPoint = mOrigin + QVector3D::dotProduct( point - mOrigin, mDirection ) * mDirection;
// project point onto the ray
QVector3D projPoint = projectedPoint( point );

// calculate the angle between the point and the projected point
QVector3D v1 = projectedPoint - mOrigin ;
QVector3D v2 = point - mOrigin;
QVector3D v1 = projPoint - mOrigin ;
QVector3D v2 = point - projPoint;
return qRadiansToDegrees( std::atan2( v2.length(), v1.length() ) );
}
2 changes: 1 addition & 1 deletion src/core/geometry/qgsray3d.h
Expand Up @@ -31,7 +31,7 @@ class CORE_EXPORT QgsRay3D
{
public:
/*
* Constructor
* Constructor
* \note : the direction is automatically normalized
*/
QgsRay3D( const QVector3D &origin, const QVector3D &direction );
Expand Down

0 comments on commit 05735cd

Please sign in to comment.