QGIS API Documentation 4.1.0-Master (376402f9aeb)
Loading...
Searching...
No Matches
qgspointcloud3dsymbol_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloud3dsymbol_p.cpp
3 ------------------------------
4 Date : December 2020
5 Copyright : (C) 2020 by Nedjima Belgacem
6 Email : belgacem dot nedjima at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include <memory>
19
20#include "delaunator.hpp"
21#include "qgs3dutils.h"
22#include "qgsfeedback.h"
23#include "qgsgeotransform.h"
24#include "qgsmaterial.h"
28#include "qgspointcloudindex.h"
31
32#include <QPointSize>
33#include <QString>
34#include <QUrl>
35#include <Qt3DCore/QAttribute>
36#include <Qt3DCore/QBuffer>
37#include <Qt3DCore/QEntity>
38#include <Qt3DCore/QGeometry>
39#include <Qt3DRender/QEffect>
40#include <Qt3DRender/QGeometryRenderer>
41#include <Qt3DRender/QGraphicsApiFilter>
42#include <Qt3DRender/QParameter>
43#include <Qt3DRender/QShaderProgram>
44#include <Qt3DRender/QTechnique>
45
46#include "moc_qgspointcloud3dsymbol_p.cpp"
47
48using namespace Qt::StringLiterals;
49
51
52// pick a point that we'll use as origin for coordinates for this node's points
53static QgsVector3D originFromNodeBounds( QgsPointCloudIndex &pc, QgsPointCloudNodeId n, const QgsPointCloud3DRenderContext &context )
54{
55 QgsBox3D bounds = pc.getNode( n ).bounds();
56 double nodeOriginX = bounds.xMinimum();
57 double nodeOriginY = bounds.yMinimum();
58 double nodeOriginZ = bounds.zMinimum() * context.zValueScale() + context.zValueFixedOffset();
59 try
60 {
61 context.coordinateTransform().transformInPlace( nodeOriginX, nodeOriginY, nodeOriginZ );
62 }
63 catch ( QgsCsException & )
64 {
65 QgsDebugError( u"Error transforming node origin point"_s );
66 }
67 return QgsVector3D( nodeOriginX, nodeOriginY, nodeOriginZ );
68}
69
70
71QgsPointCloud3DGeometry::QgsPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
72 : Qt3DCore::QGeometry( parent )
73 , mPositionAttribute( new Qt3DCore::QAttribute( this ) )
74 , mParameterAttribute( new Qt3DCore::QAttribute( this ) )
75 , mPointSizeAttribute( new Qt3DCore::QAttribute( this ) )
76 , mColorAttribute( new Qt3DCore::QAttribute( this ) )
77 , mTriangleIndexAttribute( new Qt3DCore::QAttribute( this ) )
78 , mNormalsAttribute( new Qt3DCore::QAttribute( this ) )
79 , mVertexBuffer( new Qt3DCore::QBuffer( this ) )
80 , mByteStride( byteStride )
81{
82 if ( !data.triangles.isEmpty() )
83 {
84 mTriangleBuffer = new Qt3DCore::QBuffer( this );
85 mTriangleIndexAttribute->setAttributeType( Qt3DCore::QAttribute::IndexAttribute );
86 mTriangleIndexAttribute->setBuffer( mTriangleBuffer );
87 mTriangleIndexAttribute->setVertexBaseType( Qt3DCore::QAttribute::UnsignedInt );
88 mTriangleBuffer->setData( data.triangles );
89 mTriangleIndexAttribute->setCount( data.triangles.size() / sizeof( quint32 ) );
90 addAttribute( mTriangleIndexAttribute );
91 }
92
93 if ( !data.normals.isEmpty() )
94 {
95 mNormalsBuffer = new Qt3DCore::QBuffer( this );
96 mNormalsAttribute->setName( Qt3DCore::QAttribute::defaultNormalAttributeName() );
97 mNormalsAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
98 mNormalsAttribute->setVertexSize( 3 );
99 mNormalsAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
100 mNormalsAttribute->setBuffer( mNormalsBuffer );
101 mNormalsBuffer->setData( data.normals );
102 mNormalsAttribute->setCount( data.normals.size() / ( 3 * sizeof( float ) ) );
103 addAttribute( mNormalsAttribute );
104 }
105}
106
107QgsSingleColorPointCloud3DGeometry::QgsSingleColorPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
108 : QgsPointCloud3DGeometry( parent, data, byteStride )
109{
110 mPositionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
111 mPositionAttribute->setBuffer( mVertexBuffer );
112 mPositionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
113 mPositionAttribute->setVertexSize( 3 );
114 mPositionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
115 mPositionAttribute->setByteOffset( 0 );
116 mPositionAttribute->setByteStride( mByteStride );
117 addAttribute( mPositionAttribute );
118 makeVertexBuffer( data );
119}
120
121void QgsSingleColorPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
122{
123 QByteArray vertexBufferData;
124 vertexBufferData.resize( data.positions.size() * mByteStride );
125 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
126 int idx = 0;
127 for ( int i = 0; i < data.positions.size(); ++i )
128 {
129 rawVertexArray[idx++] = data.positions.at( i ).x();
130 rawVertexArray[idx++] = data.positions.at( i ).y();
131 rawVertexArray[idx++] = data.positions.at( i ).z();
132 }
133
134 mVertexCount = data.positions.size();
135 mVertexBuffer->setData( vertexBufferData );
136}
137
138QgsColorRampPointCloud3DGeometry::QgsColorRampPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
139 : QgsPointCloud3DGeometry( parent, data, byteStride )
140{
141 mPositionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
142 mPositionAttribute->setBuffer( mVertexBuffer );
143 mPositionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
144 mPositionAttribute->setVertexSize( 3 );
145 mPositionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
146 mPositionAttribute->setByteOffset( 0 );
147 mPositionAttribute->setByteStride( mByteStride );
148 addAttribute( mPositionAttribute );
149 mParameterAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
150 mParameterAttribute->setBuffer( mVertexBuffer );
151 mParameterAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
152 mParameterAttribute->setVertexSize( 1 );
153 mParameterAttribute->setName( "vertexParameter" );
154 mParameterAttribute->setByteOffset( 12 );
155 mParameterAttribute->setByteStride( mByteStride );
156 addAttribute( mParameterAttribute );
157 makeVertexBuffer( data );
158}
159
160void QgsColorRampPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
161{
162 QByteArray vertexBufferData;
163 vertexBufferData.resize( data.positions.size() * mByteStride );
164 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
165 int idx = 0;
166 Q_ASSERT( data.positions.size() == data.parameter.size() );
167 for ( int i = 0; i < data.positions.size(); ++i )
168 {
169 rawVertexArray[idx++] = data.positions.at( i ).x();
170 rawVertexArray[idx++] = data.positions.at( i ).y();
171 rawVertexArray[idx++] = data.positions.at( i ).z();
172 rawVertexArray[idx++] = data.parameter.at( i );
173 }
174
175 mVertexCount = data.positions.size();
176 mVertexBuffer->setData( vertexBufferData );
177}
178
179QgsRGBPointCloud3DGeometry::QgsRGBPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
180 : QgsPointCloud3DGeometry( parent, data, byteStride )
181{
182 mPositionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
183 mPositionAttribute->setBuffer( mVertexBuffer );
184 mPositionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
185 mPositionAttribute->setVertexSize( 3 );
186 mPositionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
187 mPositionAttribute->setByteOffset( 0 );
188 mPositionAttribute->setByteStride( mByteStride );
189 addAttribute( mPositionAttribute );
190 mColorAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
191 mColorAttribute->setBuffer( mVertexBuffer );
192 mColorAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
193 mColorAttribute->setVertexSize( 3 );
194 mColorAttribute->setName( u"vertexColor"_s );
195 mColorAttribute->setByteOffset( 12 );
196 mColorAttribute->setByteStride( mByteStride );
197 addAttribute( mColorAttribute );
198 makeVertexBuffer( data );
199}
200
201void QgsRGBPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
202{
203 QByteArray vertexBufferData;
204 vertexBufferData.resize( data.positions.size() * mByteStride );
205 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
206 int idx = 0;
207 Q_ASSERT( data.positions.size() == data.colors.size() );
208 for ( int i = 0; i < data.positions.size(); ++i )
209 {
210 rawVertexArray[idx++] = data.positions.at( i ).x();
211 rawVertexArray[idx++] = data.positions.at( i ).y();
212 rawVertexArray[idx++] = data.positions.at( i ).z();
213 rawVertexArray[idx++] = data.colors.at( i ).x();
214 rawVertexArray[idx++] = data.colors.at( i ).y();
215 rawVertexArray[idx++] = data.colors.at( i ).z();
216 }
217 mVertexCount = data.positions.size();
218 mVertexBuffer->setData( vertexBufferData );
219}
220
221QgsClassificationPointCloud3DGeometry::QgsClassificationPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
222 : QgsPointCloud3DGeometry( parent, data, byteStride )
223{
224 mPositionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
225 mPositionAttribute->setBuffer( mVertexBuffer );
226 mPositionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
227 mPositionAttribute->setVertexSize( 3 );
228 mPositionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
229 mPositionAttribute->setByteOffset( 0 );
230 mPositionAttribute->setByteStride( mByteStride );
231 addAttribute( mPositionAttribute );
232 mParameterAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
233 mParameterAttribute->setBuffer( mVertexBuffer );
234 mParameterAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
235 mParameterAttribute->setVertexSize( 1 );
236 mParameterAttribute->setName( "vertexParameter" );
237 mParameterAttribute->setByteOffset( 12 );
238 mParameterAttribute->setByteStride( mByteStride );
239 addAttribute( mParameterAttribute );
240 mPointSizeAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
241 mPointSizeAttribute->setBuffer( mVertexBuffer );
242 mPointSizeAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
243 mPointSizeAttribute->setVertexSize( 1 );
244 mPointSizeAttribute->setName( "vertexSize" );
245 mPointSizeAttribute->setByteOffset( 16 );
246 mPointSizeAttribute->setByteStride( mByteStride );
247 addAttribute( mPointSizeAttribute );
248 makeVertexBuffer( data );
249}
250
251void QgsClassificationPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
252{
253 QByteArray vertexBufferData;
254 vertexBufferData.resize( data.positions.size() * mByteStride );
255 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
256 int idx = 0;
257 Q_ASSERT( data.positions.size() == data.parameter.size() );
258 Q_ASSERT( data.positions.size() == data.pointSizes.size() );
259 for ( int i = 0; i < data.positions.size(); ++i )
260 {
261 rawVertexArray[idx++] = data.positions.at( i ).x();
262 rawVertexArray[idx++] = data.positions.at( i ).y();
263 rawVertexArray[idx++] = data.positions.at( i ).z();
264 rawVertexArray[idx++] = data.parameter.at( i );
265 rawVertexArray[idx++] = data.pointSizes.at( i );
266 }
267
268 mVertexCount = data.positions.size();
269 mVertexBuffer->setData( vertexBufferData );
270}
271
272QgsPointCloud3DSymbolHandler::QgsPointCloud3DSymbolHandler()
273{}
274
275
276void QgsPointCloud3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context, const QgsPointCloud3DSymbolHandler::PointData &out, bool selected )
277{
278 Q_UNUSED( selected )
279
280 if ( out.positions.empty() )
281 return;
282
283 // Geometry
284 Qt3DCore::QGeometry *geom = makeGeometry( parent, out, context.symbol()->byteStride() );
285 Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer;
286 if ( context.symbol()->renderAsTriangles() && !out.triangles.isEmpty() )
287 {
288 gr->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
289 gr->setVertexCount( out.triangles.size() / sizeof( quint32 ) );
290 }
291 else
292 {
293 gr->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
294 gr->setVertexCount( out.positions.count() );
295 }
296 gr->setGeometry( geom );
297
298 // Transform: chunks are using coordinates relative to chunk origin, with X,Y,Z axes being the same
299 // as map coordinates, so we need to rotate and translate entities to get them into world coordinates
300 QgsGeoTransform *tr = new QgsGeoTransform;
301 tr->setGeoTranslation( out.positionsOrigin );
302
303 // Material
304 QgsMaterial *mat = new QgsMaterial;
305 if ( context.symbol() )
306 context.symbol()->fillMaterial( mat );
307
308 Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram( mat );
309 shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/pointcloud.vert"_s ) ) );
310 shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/pointcloud.frag"_s ) ) );
311
312 Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass( mat );
313 renderPass->setShaderProgram( shaderProgram );
314
315 if ( out.triangles.isEmpty() )
316 {
317 Qt3DRender::QPointSize *pointSize = new Qt3DRender::QPointSize( renderPass );
318 pointSize->setSizeMode( Qt3DRender::QPointSize::Programmable ); // supported since OpenGL 3.2
319 renderPass->addRenderState( pointSize );
320 }
321
322 // without this filter the default forward renderer would not render this
323 Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
324 filterKey->setName( u"renderingStyle"_s );
325 filterKey->setValue( "forward" );
326
327 Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
328 technique->addRenderPass( renderPass );
329 technique->addFilterKey( filterKey );
330 technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
331 technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
332 technique->graphicsApiFilter()->setMajorVersion( 3 );
333 technique->graphicsApiFilter()->setMinorVersion( 1 );
334 technique->addParameter( new Qt3DRender::QParameter( "triangulate", !out.triangles.isEmpty() ) );
335
336 Qt3DRender::QEffect *eff = new Qt3DRender::QEffect;
337 eff->addTechnique( technique );
338 mat->setEffect( eff );
339
340 // All together
341 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
342 entity->addComponent( gr );
343 entity->addComponent( tr );
344 entity->addComponent( mat );
345 entity->setParent( parent );
346 // cppcheck wrongly believes entity will leak
347 // cppcheck-suppress memleak
348}
349
350
351std::vector<double> QgsPointCloud3DSymbolHandler::getVertices( QgsPointCloudIndex &pc, QgsPointCloudNodeId n, const QgsPointCloud3DRenderContext &context, const QgsBox3D &box3D )
352{
353 bool hasColorData = !outNormal.colors.empty();
354 bool hasParameterData = !outNormal.parameter.empty();
355 bool hasPointSizeData = !outNormal.pointSizes.empty();
356
357 // first, get the points of the concerned node
358 std::vector<double> vertices( outNormal.positions.size() * 2 );
359 size_t idx = 0;
360 for ( int i = 0; i < outNormal.positions.size(); ++i )
361 {
362 vertices[idx++] = outNormal.positions.at( i ).x();
363 vertices[idx++] = -outNormal.positions.at( i ).y(); // flipping y to have correctly oriented triangles from delaunator
364 }
365
366 // next, we also need all points of all parents nodes to make the triangulation (also external points)
367 QgsPointCloudNodeId parentNode = n.parentNode();
368
369 double span = pc.span();
370 //factor to take account of the density of the point to calculate extension of the bounding box
371 // with a usual value span = 128, bounding box is extended by 12.5 % on each side.
372 double extraBoxFactor = 16 / span;
373
374 // We keep all points in vertical direction to avoid odd triangulation if points are isolated on top
375 QgsRectangle rectRelativeToChunkOrigin = ( box3D - outNormal.positionsOrigin ).toRectangle();
376 rectRelativeToChunkOrigin.grow( extraBoxFactor * std::max( box3D.width(), box3D.height() ) );
377
378 PointData filteredExtraPointData;
379 while ( parentNode.d() >= 0 )
380 {
381 PointData outputParent;
382 processNode( pc, parentNode, context, &outputParent );
383
384 // the "main" chunk and each parent chunks have their origins
385 QVector3D originDifference = ( outputParent.positionsOrigin - outNormal.positionsOrigin ).toVector3D();
386
387 for ( int i = 0; i < outputParent.positions.count(); ++i )
388 {
389 const QVector3D pos = outputParent.positions.at( i ) + originDifference;
390 if ( rectRelativeToChunkOrigin.contains( pos.x(), pos.y() ) )
391 {
392 filteredExtraPointData.positions.append( pos );
393 vertices.push_back( pos.x() );
394 vertices.push_back( -pos.y() ); // flipping y to have correctly oriented triangles from delaunator
395
396 if ( hasColorData )
397 filteredExtraPointData.colors.append( outputParent.colors.at( i ) );
398 if ( hasParameterData )
399 filteredExtraPointData.parameter.append( outputParent.parameter.at( i ) );
400 if ( hasPointSizeData )
401 filteredExtraPointData.pointSizes.append( outputParent.pointSizes.at( i ) );
402 }
403 }
404
405 parentNode = parentNode.parentNode();
406 }
407
408 outNormal.positions.append( filteredExtraPointData.positions );
409 outNormal.colors.append( filteredExtraPointData.colors );
410 outNormal.parameter.append( filteredExtraPointData.parameter );
411 outNormal.pointSizes.append( filteredExtraPointData.pointSizes );
412
413 return vertices;
414}
415
416void QgsPointCloud3DSymbolHandler::calculateNormals( const std::vector<size_t> &triangles )
417{
418 QVector<QVector3D> normals( outNormal.positions.count(), QVector3D( 0.0, 0.0, 0.0 ) );
419 for ( size_t i = 0; i < triangles.size(); i += 3 )
420 {
421 QVector<QVector3D> triangleVertices( 3 );
422 for ( size_t j = 0; j < 3; ++j )
423 {
424 size_t vertIndex = triangles.at( i + j );
425 triangleVertices[j] = outNormal.positions.at( vertIndex );
426 }
427 //calculate normals
428 for ( size_t j = 0; j < 3; ++j )
429 normals[triangles.at( i + j )] += QVector3D::crossProduct( triangleVertices.at( 1 ) - triangleVertices.at( 0 ), triangleVertices.at( 2 ) - triangleVertices.at( 0 ) );
430 }
431
432 // Build now normals array
433 outNormal.normals.resize( ( outNormal.positions.count() ) * sizeof( float ) * 3 );
434 float *normPtr = reinterpret_cast<float *>( outNormal.normals.data() );
435 for ( int i = 0; i < normals.size(); ++i )
436 {
437 QVector3D normal = normals.at( i );
438 normal = normal.normalized();
439
440 *normPtr++ = normal.x();
441 *normPtr++ = normal.y();
442 *normPtr++ = normal.z();
443 }
444}
445
446void QgsPointCloud3DSymbolHandler::filterTriangles( const std::vector<size_t> &triangleIndexes, const QgsPointCloud3DRenderContext &context, const QgsBox3D &box3D )
447{
448 outNormal.triangles.resize( triangleIndexes.size() * sizeof( quint32 ) );
449 quint32 *indexPtr = reinterpret_cast<quint32 *>( outNormal.triangles.data() );
450 size_t effective = 0;
451
452 bool horizontalFilter = context.symbol()->horizontalTriangleFilter();
453 bool verticalFilter = context.symbol()->verticalTriangleFilter();
454 float horizontalThreshold = context.symbol()->horizontalFilterThreshold();
455 float verticalThreshold = context.symbol()->verticalFilterThreshold();
456
457 QgsBox3D boxRelativeToChunkOrigin = box3D - outNormal.positionsOrigin;
458
459 for ( size_t i = 0; i < triangleIndexes.size(); i += 3 )
460 {
461 bool atLeastOneInBox = false;
462 bool horizontalSkip = false;
463 bool verticalSkip = false;
464 for ( size_t j = 0; j < 3; j++ )
465 {
466 QVector3D pos = outNormal.positions.at( triangleIndexes.at( i + j ) );
467 atLeastOneInBox |= boxRelativeToChunkOrigin.contains( pos.x(), pos.y(), pos.z() );
468
469 if ( verticalFilter || horizontalFilter )
470 {
471 const QVector3D pos2 = outNormal.positions.at( triangleIndexes.at( i + ( j + 1 ) % 3 ) );
472
473 if ( verticalFilter )
474 verticalSkip |= std::fabs( pos.z() - pos2.z() ) > verticalThreshold;
475
476 if ( horizontalFilter && !verticalSkip )
477 {
478 // filter only in the horizontal plan, it is a 2.5D triangulation.
479 horizontalSkip |= sqrt( std::pow( pos.x() - pos2.x(), 2 ) + std::pow( pos.y() - pos2.y(), 2 ) ) > horizontalThreshold;
480 }
481
482 if ( horizontalSkip || verticalSkip )
483 break;
484 }
485 }
486 if ( atLeastOneInBox && !horizontalSkip && !verticalSkip )
487 {
488 for ( size_t j = 0; j < 3; j++ )
489 {
490 size_t vertIndex = triangleIndexes.at( i + j );
491 *indexPtr++ = quint32( vertIndex );
492 }
493 effective++;
494 }
495 }
496
497 if ( effective != 0 )
498 {
499 outNormal.triangles.resize( effective * 3 * sizeof( quint32 ) );
500 }
501 else
502 {
503 outNormal.triangles.clear();
504 outNormal.normals.clear();
505 }
506}
507
508void QgsPointCloud3DSymbolHandler::triangulate( QgsPointCloudIndex &pc, QgsPointCloudNodeId n, const QgsPointCloud3DRenderContext &context, const QgsBox3D &box3D )
509{
510 if ( outNormal.positions.isEmpty() )
511 return;
512
513 // Triangulation happens here
514 std::unique_ptr<delaunator::Delaunator> triangulation;
515 try
516 {
517 std::vector<double> vertices = getVertices( pc, n, context, box3D );
518 triangulation = std::make_unique<delaunator::Delaunator>( vertices );
519 }
520 catch ( std::exception &e )
521 {
522 // something went wrong, better to retrieve initial state
523 QgsDebugMsgLevel( u"Error with triangulation"_s, 4 );
524 outNormal = PointData();
525 processNode( pc, n, context );
526 return;
527 }
528
529 const std::vector<size_t> &triangleIndexes = triangulation->triangles;
530
531 calculateNormals( triangleIndexes );
532 filterTriangles( triangleIndexes, context, box3D );
533}
534
535std::unique_ptr<QgsPointCloudBlock> QgsPointCloud3DSymbolHandler::pointCloudBlock(
537)
538{
539 std::unique_ptr<QgsPointCloudBlock> block;
541 {
542 block = pc.nodeData( n, request );
543 }
545 {
546 QgsPointCloudNode node = pc.getNode( n );
547 if ( node.pointCount() < 1 )
548 return block;
549
550 bool loopAborted = false;
551 QEventLoop loop;
552 QgsPointCloudBlockRequest *req = pc.asyncNodeData( n, request );
553 QObject::connect( req, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
554 QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, [&]() {
555 loopAborted = true;
556 loop.quit();
557 } );
558 loop.exec();
559
560 if ( !loopAborted )
561 block = req->takeBlock();
562 }
563 return block;
564}
565
566//
567
568QgsSingleColorPointCloud3DSymbolHandler::QgsSingleColorPointCloud3DSymbolHandler()
569 : QgsPointCloud3DSymbolHandler()
570{}
571
572bool QgsSingleColorPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
573{
574 Q_UNUSED( context )
575 return true;
576}
577
578void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex &pc, QgsPointCloudNodeId n, const QgsPointCloud3DRenderContext &context, PointData *output )
579{
584
585 QgsPointCloudRequest request;
586 request.setAttributes( attributes );
587 request.setFilterRect( context.layerExtent() );
588 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
589 if ( !block )
590 return;
591
592 const char *ptr = block->data();
593 const int count = block->pointCount();
594 const std::size_t recordSize = block->attributes().pointRecordSize();
595 const QgsVector3D blockScale = block->scale();
596 const QgsVector3D blockOffset = block->offset();
597 const double zValueScale = context.zValueScale();
598 const double zValueOffset = context.zValueFixedOffset();
599 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
600 bool alreadyPrintedDebug = false;
601
602 if ( !output )
603 output = &outNormal;
604
605 output->positionsOrigin = originFromNodeBounds( pc, n, context );
606
607 for ( int i = 0; i < count; ++i )
608 {
609 if ( context.isCanceled() )
610 break;
611
612 const qint32 ix = *( qint32 * ) ( ptr + i * recordSize + 0 );
613 const qint32 iy = *( qint32 * ) ( ptr + i * recordSize + 4 );
614 const qint32 iz = *( qint32 * ) ( ptr + i * recordSize + 8 );
615
616 double x = blockOffset.x() + blockScale.x() * ix;
617 double y = blockOffset.y() + blockScale.y() * iy;
618 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
619 try
620 {
621 coordinateTransform.transformInPlace( x, y, z );
622 }
623 catch ( QgsCsException &e )
624 {
625 if ( !alreadyPrintedDebug )
626 {
627 QgsDebugError( u"Error transforming point coordinate"_s );
628 alreadyPrintedDebug = true;
629 }
630 }
631 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
632 output->positions.push_back( point.toVector3D() );
633 }
634}
635
636void QgsSingleColorPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
637{
638 makeEntity( parent, context, outNormal, false );
639}
640
641Qt3DCore::QGeometry *QgsSingleColorPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
642{
643 return new QgsSingleColorPointCloud3DGeometry( parent, data, byteStride );
644}
645
646QgsColorRampPointCloud3DSymbolHandler::QgsColorRampPointCloud3DSymbolHandler()
647 : QgsPointCloud3DSymbolHandler()
648{}
649
650bool QgsColorRampPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
651{
652 Q_UNUSED( context )
653 return true;
654}
655
656void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex &pc, QgsPointCloudNodeId n, const QgsPointCloud3DRenderContext &context, PointData *output )
657{
659 const int xOffset = 0;
661 const int yOffset = attributes.pointRecordSize();
663 const int zOffset = attributes.pointRecordSize();
665
666 QString attributeName;
667 bool attrIsX = false;
668 bool attrIsY = false;
669 bool attrIsZ = false;
671 int attributeOffset = 0;
672 const double zValueScale = context.zValueScale();
673 const double zValueOffset = context.zValueFixedOffset();
674 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
675 bool alreadyPrintedDebug = false;
676
677 QgsColorRampPointCloud3DSymbol *symbol = dynamic_cast<QgsColorRampPointCloud3DSymbol *>( context.symbol() );
678 if ( symbol )
679 {
680 int offset = 0;
681 const QgsPointCloudAttributeCollection collection = context.attributes();
682
683 if ( symbol->attribute() == "X"_L1 )
684 {
685 attrIsX = true;
686 }
687 else if ( symbol->attribute() == "Y"_L1 )
688 {
689 attrIsY = true;
690 }
691 else if ( symbol->attribute() == "Z"_L1 )
692 {
693 attrIsZ = true;
694 }
695 else
696 {
697 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
698 if ( attr )
699 {
700 attributeType = attr->type();
701 attributeName = attr->name();
702 attributeOffset = attributes.pointRecordSize();
703 attributes.push_back( *attr );
704 }
705 }
706 }
707
708 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
709 return;
710
711 QgsPointCloudRequest request;
712 request.setAttributes( attributes );
713 request.setFilterRect( context.layerExtent() );
714 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
715 if ( !block )
716 return;
717
718 const char *ptr = block->data();
719 const int count = block->pointCount();
720 const std::size_t recordSize = block->attributes().pointRecordSize();
721
722 const QgsVector3D blockScale = block->scale();
723 const QgsVector3D blockOffset = block->offset();
724
725 if ( !output )
726 output = &outNormal;
727
728 output->positionsOrigin = originFromNodeBounds( pc, n, context );
729
730 for ( int i = 0; i < count; ++i )
731 {
732 if ( context.isCanceled() )
733 break;
734
735 const qint32 ix = *( qint32 * ) ( ptr + i * recordSize + xOffset );
736 const qint32 iy = *( qint32 * ) ( ptr + i * recordSize + yOffset );
737 const qint32 iz = *( qint32 * ) ( ptr + i * recordSize + zOffset );
738
739 double x = blockOffset.x() + blockScale.x() * ix;
740 double y = blockOffset.y() + blockScale.y() * iy;
741 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
742 try
743 {
744 coordinateTransform.transformInPlace( x, y, z );
745 }
746 catch ( QgsCsException & )
747 {
748 if ( !alreadyPrintedDebug )
749 {
750 QgsDebugError( u"Error transforming point coordinate"_s );
751 alreadyPrintedDebug = true;
752 }
753 }
754 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
755 output->positions.push_back( point.toVector3D() );
756
757 if ( attrIsX )
758 output->parameter.push_back( x );
759 else if ( attrIsY )
760 output->parameter.push_back( y );
761 else if ( attrIsZ )
762 output->parameter.push_back( z );
763 else
764 {
765 float iParam = 0.0f;
766 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
767 output->parameter.push_back( iParam );
768 }
769 }
770}
771
772void QgsColorRampPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
773{
774 makeEntity( parent, context, outNormal, false );
775}
776
777Qt3DCore::QGeometry *QgsColorRampPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
778{
779 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
780}
781
782QgsRGBPointCloud3DSymbolHandler::QgsRGBPointCloud3DSymbolHandler()
783 : QgsPointCloud3DSymbolHandler()
784{}
785
786bool QgsRGBPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
787{
788 Q_UNUSED( context )
789 return true;
790}
791
792void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex &pc, QgsPointCloudNodeId n, const QgsPointCloud3DRenderContext &context, PointData *output )
793{
798
799 QgsRgbPointCloud3DSymbol *symbol = qgis::down_cast<QgsRgbPointCloud3DSymbol *>( context.symbol() );
800
801 // we have to get the RGB attributes using their real data types -- they aren't always short! (sometimes unsigned short)
802 int attrOffset = 0;
803
804 const int redOffset = attributes.pointRecordSize();
805 const QgsPointCloudAttribute *colorAttribute = context.attributes().find( symbol->redAttribute(), attrOffset );
806 attributes.push_back( *colorAttribute );
807 const QgsPointCloudAttribute::DataType redType = colorAttribute->type();
808
809 const int greenOffset = attributes.pointRecordSize();
810 colorAttribute = context.attributes().find( symbol->greenAttribute(), attrOffset );
811 attributes.push_back( *colorAttribute );
812 const QgsPointCloudAttribute::DataType greenType = colorAttribute->type();
813
814 const int blueOffset = attributes.pointRecordSize();
815 colorAttribute = context.attributes().find( symbol->blueAttribute(), attrOffset );
816 attributes.push_back( *colorAttribute );
817 const QgsPointCloudAttribute::DataType blueType = colorAttribute->type();
818
819 QgsPointCloudRequest request;
820 request.setAttributes( attributes );
821 request.setFilterRect( context.layerExtent() );
822 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
823 if ( !block )
824 return;
825
826 const char *ptr = block->data();
827 const int count = block->pointCount();
828 const std::size_t recordSize = block->attributes().pointRecordSize();
829
830 const QgsVector3D blockScale = block->scale();
831 const QgsVector3D blockOffset = block->offset();
832 const double zValueScale = context.zValueScale();
833 const double zValueOffset = context.zValueFixedOffset();
834 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
835 bool alreadyPrintedDebug = false;
836
837 QgsContrastEnhancement *redContrastEnhancement = symbol->redContrastEnhancement();
838 QgsContrastEnhancement *greenContrastEnhancement = symbol->greenContrastEnhancement();
839 QgsContrastEnhancement *blueContrastEnhancement = symbol->blueContrastEnhancement();
840
841 const bool useRedContrastEnhancement = redContrastEnhancement && redContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
842 const bool useBlueContrastEnhancement = blueContrastEnhancement && blueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
843 const bool useGreenContrastEnhancement = greenContrastEnhancement && greenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
844
845 if ( !output )
846 output = &outNormal;
847
848 output->positionsOrigin = originFromNodeBounds( pc, n, context );
849
850 int ir = 0;
851 int ig = 0;
852 int ib = 0;
853 for ( int i = 0; i < count; ++i )
854 {
855 if ( context.isCanceled() )
856 break;
857
858 const qint32 ix = *( qint32 * ) ( ptr + i * recordSize + 0 );
859 const qint32 iy = *( qint32 * ) ( ptr + i * recordSize + 4 );
860 const qint32 iz = *( qint32 * ) ( ptr + i * recordSize + 8 );
861 double x = blockOffset.x() + blockScale.x() * ix;
862 double y = blockOffset.y() + blockScale.y() * iy;
863 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
864 try
865 {
866 coordinateTransform.transformInPlace( x, y, z );
867 }
868 catch ( QgsCsException & )
869 {
870 if ( !alreadyPrintedDebug )
871 {
872 QgsDebugError( u"Error transforming point coordinate"_s );
873 alreadyPrintedDebug = true;
874 }
875 }
876 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
877
878 QVector3D color( 0.0f, 0.0f, 0.0f );
879
880 context.getAttribute( ptr, i * recordSize + redOffset, redType, ir );
881 context.getAttribute( ptr, i * recordSize + greenOffset, greenType, ig );
882 context.getAttribute( ptr, i * recordSize + blueOffset, blueType, ib );
883
884 //skip if red, green or blue not in displayable range
885 if ( ( useRedContrastEnhancement && !redContrastEnhancement->isValueInDisplayableRange( ir ) )
886 || ( useGreenContrastEnhancement && !greenContrastEnhancement->isValueInDisplayableRange( ig ) )
887 || ( useBlueContrastEnhancement && !blueContrastEnhancement->isValueInDisplayableRange( ib ) ) )
888 {
889 continue;
890 }
891
892 //stretch color values
893 if ( useRedContrastEnhancement )
894 {
895 ir = redContrastEnhancement->enhanceContrast( ir );
896 }
897 if ( useGreenContrastEnhancement )
898 {
899 ig = greenContrastEnhancement->enhanceContrast( ig );
900 }
901 if ( useBlueContrastEnhancement )
902 {
903 ib = blueContrastEnhancement->enhanceContrast( ib );
904 }
905
906 const QColor linear = Qgs3DUtils::srgbToLinear( QColor( ir, ig, ib ) );
907 color.setX( linear.redF() );
908 color.setY( linear.greenF() );
909 color.setZ( linear.blueF() );
910
911 output->positions.push_back( point.toVector3D() );
912 output->colors.push_back( color );
913 }
914}
915
916void QgsRGBPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
917{
918 makeEntity( parent, context, outNormal, false );
919}
920
921Qt3DCore::QGeometry *QgsRGBPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
922{
923 return new QgsRGBPointCloud3DGeometry( parent, data, byteStride );
924}
925
926QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
927 : QgsPointCloud3DSymbolHandler()
928{}
929
930bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
931{
932 Q_UNUSED( context )
933 return true;
934}
935
936void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex &pc, QgsPointCloudNodeId n, const QgsPointCloud3DRenderContext &context, PointData *output )
937{
939 const int xOffset = 0;
941 const int yOffset = attributes.pointRecordSize();
943 const int zOffset = attributes.pointRecordSize();
945
946 QString attributeName;
947 bool attrIsX = false;
948 bool attrIsY = false;
949 bool attrIsZ = false;
951 int attributeOffset = 0;
953 if ( !symbol )
954 return;
955
956 int offset = 0;
957 const QgsPointCloudAttributeCollection collection = context.attributes();
958
959 if ( symbol->attribute() == "X"_L1 )
960 {
961 attrIsX = true;
962 }
963 else if ( symbol->attribute() == "Y"_L1 )
964 {
965 attrIsY = true;
966 }
967 else if ( symbol->attribute() == "Z"_L1 )
968 {
969 attrIsZ = true;
970 }
971 else
972 {
973 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
974 if ( attr )
975 {
976 attributeType = attr->type();
977 attributeName = attr->name();
978 attributeOffset = attributes.pointRecordSize();
979 attributes.push_back( *attr );
980 }
981 }
982
983 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
984 return;
985
986 QgsPointCloudRequest request;
987 request.setAttributes( attributes );
988 request.setFilterRect( context.layerExtent() );
989 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
990 if ( !block )
991 return;
992
993 const char *ptr = block->data();
994 const int count = block->pointCount();
995 const std::size_t recordSize = block->attributes().pointRecordSize();
996
997 const QgsVector3D blockScale = block->scale();
998 const QgsVector3D blockOffset = block->offset();
999 const double zValueScale = context.zValueScale();
1000 const double zValueOffset = context.zValueFixedOffset();
1001 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
1002 bool alreadyPrintedDebug = false;
1003
1004 QList<QgsPointCloudCategory> categoriesList = symbol->categoriesList();
1005 QVector<int> categoriesValues;
1006 QHash<int, double> categoriesPointSizes;
1007 for ( QgsPointCloudCategory &c : categoriesList )
1008 {
1009 categoriesValues.push_back( c.value() );
1010 categoriesPointSizes.insert( c.value(), c.pointSize() > 0 ? c.pointSize() : context.symbol() ? context.symbol()->pointSize() : 1.0 );
1011 }
1012
1013 if ( !output )
1014 output = &outNormal;
1015
1016 output->positionsOrigin = originFromNodeBounds( pc, n, context );
1017
1018 const QSet<int> filteredOutValues = context.getFilteredOutValues();
1019 for ( int i = 0; i < count; ++i )
1020 {
1021 if ( context.isCanceled() )
1022 break;
1023
1024 const qint32 ix = *( qint32 * ) ( ptr + i * recordSize + xOffset );
1025 const qint32 iy = *( qint32 * ) ( ptr + i * recordSize + yOffset );
1026 const qint32 iz = *( qint32 * ) ( ptr + i * recordSize + zOffset );
1027
1028 double x = blockOffset.x() + blockScale.x() * ix;
1029 double y = blockOffset.y() + blockScale.y() * iy;
1030 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
1031 try
1032 {
1033 coordinateTransform.transformInPlace( x, y, z );
1034 }
1035 catch ( QgsCsException & )
1036 {
1037 if ( !alreadyPrintedDebug )
1038 {
1039 QgsDebugError( u"Error transforming point coordinate"_s );
1040 alreadyPrintedDebug = true;
1041 }
1042 }
1043 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
1044 float iParam = 0.0f;
1045 if ( attrIsX )
1046 iParam = x;
1047 else if ( attrIsY )
1048 iParam = y;
1049 else if ( attrIsZ )
1050 iParam = z;
1051 else
1052 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
1053
1054 if ( filteredOutValues.contains( ( int ) iParam ) || !categoriesValues.contains( ( int ) iParam ) )
1055 continue;
1056 output->positions.push_back( point.toVector3D() );
1057
1058 // find iParam actual index in the categories list
1059 float iParam2 = categoriesValues.indexOf( ( int ) iParam ) + 1;
1060 output->parameter.push_back( iParam2 );
1061 output->pointSizes.push_back( categoriesPointSizes.value( ( int ) iParam ) );
1062 }
1063}
1064
1065void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
1066{
1067 makeEntity( parent, context, outNormal, false );
1068}
1069
1070Qt3DCore::QGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
1071{
1072 return new QgsClassificationPointCloud3DGeometry( parent, data, byteStride );
1073}
1074
@ Local
Local means the source is a local file on the machine.
Definition qgis.h:6589
@ Remote
Remote means it's loaded through a protocol like HTTP.
Definition qgis.h:6590
static QColor srgbToLinear(const QColor &color)
Converts a SRGB color to a linear color.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:205
bool contains(const QgsBox3D &other) const
Returns true when box contains other box.
Definition qgsbox3d.cpp:164
double width() const
Returns the width of the box.
Definition qgsbox3d.h:287
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:261
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:233
double height() const
Returns the height of the box.
Definition qgsbox3d.h:294
3D symbol that draws point cloud geometries as 3D objects using classification of the dataset.
QgsPointCloudCategoryList categoriesList() const
Returns the list of categories of the classification.
QString attribute() const
Returns the attribute used to select the color of the point cloud.
3D symbol that draws point cloud geometries as 3D objects using color ramp shader.
QString attribute() const
Returns the attribute used to select the color of the point cloud.
Handles contrast enhancement and clipping.
@ NoEnhancement
Default color scaling algorithm, no scaling is applied.
bool isValueInDisplayableRange(double value)
Returns true if a pixel value is in displayable range, false if pixel is outside of range (i....
int enhanceContrast(double value)
Applies the contrast enhancement to a value.
ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
Handles coordinate transforms between two coordinate systems.
void transformInPlace(double &x, double &y, double &z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
Custom exception class for Coordinate Reference System related exceptions.
void canceled()
Internal routines can connect to this signal if they use event loop.
Base class for all materials used within QGIS 3D views.
Definition qgsmaterial.h:40
Encapsulates the render context for a 3D point cloud rendering operation.
void getAttribute(const char *data, std::size_t offset, QgsPointCloudAttribute::DataType type, T &value) const
Retrieves the attribute value from data at the specified offset, where type indicates the original da...
QSet< int > getFilteredOutValues() const
Returns a set containing the filtered out values.
QgsPointCloud3DSymbol * symbol() const
Returns the symbol used for rendering the point cloud.
double zValueScale() const
Returns any constant scaling factor which must be applied to z values taken from the point cloud inde...
QgsFeedback * feedback() const
Returns the feedback object used to cancel rendering and check if rendering was canceled.
QgsPointCloudAttributeCollection attributes() const
Returns the attributes associated with the rendered block.
bool isCanceled() const
Returns true if the rendering is canceled.
QgsCoordinateTransform coordinateTransform() const
Returns the coordinate transform used to transform points from layer CRS to the map CRS.
QgsRectangle layerExtent() const
Returns the 3D scene's extent in layer crs.
double zValueFixedOffset() const
Returns any constant offset which must be applied to z values taken from the point cloud index.
virtual void fillMaterial(QgsMaterial *material) SIP_SKIP=0
Used to fill material object with necessary QParameters (and consequently opengl uniforms).
bool verticalTriangleFilter() const
Returns whether triangles are filtered by vertical height for rendering.
float verticalFilterThreshold() const
Returns the threshold vertical height value for filtering triangles.
virtual unsigned int byteStride()=0
Returns the byte stride for the geometries used to for the vertex buffer.
float horizontalFilterThreshold() const
Returns the threshold horizontal size value for filtering triangles.
bool renderAsTriangles() const
Returns whether points are triangulated to render solid surface.
float pointSize() const
Returns the point size of the point cloud.
bool horizontalTriangleFilter() const
Returns whether triangles are filtered by horizontal size for rendering.
A collection of point cloud attributes.
void push_back(const QgsPointCloudAttribute &attribute)
Adds extra attribute.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
Attribute for point cloud data pair of name and size in bytes.
DataType
Systems of unit measurement.
QString name() const
Returns name of the attribute.
DataType type() const
Returns the data type.
Base class for handling loading QgsPointCloudBlock asynchronously.
void finished()
Emitted when the request processing has finished.
std::unique_ptr< QgsPointCloudBlock > takeBlock()
Returns the requested block.
Represents an individual category (class) from a QgsPointCloudClassifiedRenderer.
Smart pointer for QgsAbstractPointCloudIndex.
int span() const
Returns the number of points in one direction in a single node.
std::unique_ptr< QgsPointCloudBlock > nodeData(QgsPointCloudNodeId n, const QgsPointCloudRequest &request)
Returns node data block.
QgsPointCloudNode getNode(QgsPointCloudNodeId id) const
Returns object for a given node.
QgsPointCloudBlockRequest * asyncNodeData(QgsPointCloudNodeId n, const QgsPointCloudRequest &request)
Returns a handle responsible for loading a node data block.
Qgis::PointCloudAccessType accessType() const
Returns the access type of the data If the access type is Remote, data will be fetched from an HTTP s...
Represents an indexed point cloud node's position in octree.
int d() const
Returns d.
QgsPointCloudNodeId parentNode() const
Returns the parent of the node.
Keeps metadata for an indexed point cloud node.
qint64 pointCount() const
Returns number of points contained in node data.
QgsBox3D bounds() const
Returns node's bounding cube in CRS coords.
Point cloud data request.
void setFilterRect(QgsRectangle extent)
Sets the rectangle from which points will be taken, in point cloud's crs.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
void grow(double delta)
Grows the rectangle in place by the specified amount.
3D symbol that draws point cloud geometries as 3D objects using RGB colors in the dataset.
QString blueAttribute() const
Returns the attribute to use for the blue channel.
QString greenAttribute() const
Returns the attribute to use for the green channel.
QgsContrastEnhancement * blueContrastEnhancement()
Returns the contrast enhancement to use for the blue channel.
QString redAttribute() const
Returns the attribute to use for the red channel.
QgsContrastEnhancement * greenContrastEnhancement()
Returns the contrast enhancement to use for the green channel.
QgsContrastEnhancement * redContrastEnhancement()
Returns the contrast enhancement to use for the red channel.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59