QGIS API Documentation 3.99.0-Master (18a1e75d814)
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 "qgsfeedback.h"
22#include "qgsgeotransform.h"
23#include "qgsmaterial.h"
27#include "qgspointcloudindex.h"
30
31#include <QPointSize>
32#include <QString>
33#include <QUrl>
34#include <Qt3DCore/QAttribute>
35#include <Qt3DCore/QBuffer>
36#include <Qt3DCore/QEntity>
37#include <Qt3DCore/QGeometry>
38#include <Qt3DRender/QEffect>
39#include <Qt3DRender/QGeometryRenderer>
40#include <Qt3DRender/QGraphicsApiFilter>
41#include <Qt3DRender/QParameter>
42#include <Qt3DRender/QShaderProgram>
43#include <Qt3DRender/QTechnique>
44
45#include "moc_qgspointcloud3dsymbol_p.cpp"
46
47using namespace Qt::StringLiterals;
48
50
51// pick a point that we'll use as origin for coordinates for this node's points
52static QgsVector3D originFromNodeBounds( QgsPointCloudIndex &pc, const QgsPointCloudNodeId &n, const QgsPointCloud3DRenderContext &context )
53{
54 QgsBox3D bounds = pc.getNode( n ).bounds();
55 double nodeOriginX = bounds.xMinimum();
56 double nodeOriginY = bounds.yMinimum();
57 double nodeOriginZ = bounds.zMinimum() * context.zValueScale() + context.zValueFixedOffset();
58 try
59 {
60 context.coordinateTransform().transformInPlace( nodeOriginX, nodeOriginY, nodeOriginZ );
61 }
62 catch ( QgsCsException & )
63 {
64 QgsDebugError( u"Error transforming node origin point"_s );
65 }
66 return QgsVector3D( nodeOriginX, nodeOriginY, nodeOriginZ );
67}
68
69
70QgsPointCloud3DGeometry::QgsPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
71 : Qt3DCore::QGeometry( parent )
72 , mPositionAttribute( new Qt3DCore::QAttribute( this ) )
73 , mParameterAttribute( new Qt3DCore::QAttribute( this ) )
74 , mPointSizeAttribute( new Qt3DCore::QAttribute( this ) )
75 , mColorAttribute( new Qt3DCore::QAttribute( this ) )
76 , mTriangleIndexAttribute( new Qt3DCore::QAttribute( this ) )
77 , mNormalsAttribute( new Qt3DCore::QAttribute( this ) )
78 , mVertexBuffer( new Qt3DCore::QBuffer( this ) )
79 , mByteStride( byteStride )
80{
81 if ( !data.triangles.isEmpty() )
82 {
83 mTriangleBuffer = new Qt3DCore::QBuffer( this );
84 mTriangleIndexAttribute->setAttributeType( Qt3DCore::QAttribute::IndexAttribute );
85 mTriangleIndexAttribute->setBuffer( mTriangleBuffer );
86 mTriangleIndexAttribute->setVertexBaseType( Qt3DCore::QAttribute::UnsignedInt );
87 mTriangleBuffer->setData( data.triangles );
88 mTriangleIndexAttribute->setCount( data.triangles.size() / sizeof( quint32 ) );
89 addAttribute( mTriangleIndexAttribute );
90 }
91
92 if ( !data.normals.isEmpty() )
93 {
94 mNormalsBuffer = new Qt3DCore::QBuffer( this );
95 mNormalsAttribute->setName( Qt3DCore::QAttribute::defaultNormalAttributeName() );
96 mNormalsAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
97 mNormalsAttribute->setVertexSize( 3 );
98 mNormalsAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
99 mNormalsAttribute->setBuffer( mNormalsBuffer );
100 mNormalsBuffer->setData( data.normals );
101 mNormalsAttribute->setCount( data.normals.size() / ( 3 * sizeof( float ) ) );
102 addAttribute( mNormalsAttribute );
103 }
104}
105
106QgsSingleColorPointCloud3DGeometry::QgsSingleColorPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
107 : QgsPointCloud3DGeometry( parent, data, byteStride )
108{
109 mPositionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
110 mPositionAttribute->setBuffer( mVertexBuffer );
111 mPositionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
112 mPositionAttribute->setVertexSize( 3 );
113 mPositionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
114 mPositionAttribute->setByteOffset( 0 );
115 mPositionAttribute->setByteStride( mByteStride );
116 addAttribute( mPositionAttribute );
117 makeVertexBuffer( data );
118}
119
120void QgsSingleColorPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
121{
122 QByteArray vertexBufferData;
123 vertexBufferData.resize( data.positions.size() * mByteStride );
124 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
125 int idx = 0;
126 for ( int i = 0; i < data.positions.size(); ++i )
127 {
128 rawVertexArray[idx++] = data.positions.at( i ).x();
129 rawVertexArray[idx++] = data.positions.at( i ).y();
130 rawVertexArray[idx++] = data.positions.at( i ).z();
131 }
132
133 mVertexCount = data.positions.size();
134 mVertexBuffer->setData( vertexBufferData );
135}
136
137QgsColorRampPointCloud3DGeometry::QgsColorRampPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
138 : QgsPointCloud3DGeometry( parent, data, byteStride )
139{
140 mPositionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
141 mPositionAttribute->setBuffer( mVertexBuffer );
142 mPositionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
143 mPositionAttribute->setVertexSize( 3 );
144 mPositionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
145 mPositionAttribute->setByteOffset( 0 );
146 mPositionAttribute->setByteStride( mByteStride );
147 addAttribute( mPositionAttribute );
148 mParameterAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
149 mParameterAttribute->setBuffer( mVertexBuffer );
150 mParameterAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
151 mParameterAttribute->setVertexSize( 1 );
152 mParameterAttribute->setName( "vertexParameter" );
153 mParameterAttribute->setByteOffset( 12 );
154 mParameterAttribute->setByteStride( mByteStride );
155 addAttribute( mParameterAttribute );
156 makeVertexBuffer( data );
157}
158
159void QgsColorRampPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
160{
161 QByteArray vertexBufferData;
162 vertexBufferData.resize( data.positions.size() * mByteStride );
163 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
164 int idx = 0;
165 Q_ASSERT( data.positions.size() == data.parameter.size() );
166 for ( int i = 0; i < data.positions.size(); ++i )
167 {
168 rawVertexArray[idx++] = data.positions.at( i ).x();
169 rawVertexArray[idx++] = data.positions.at( i ).y();
170 rawVertexArray[idx++] = data.positions.at( i ).z();
171 rawVertexArray[idx++] = data.parameter.at( i );
172 }
173
174 mVertexCount = data.positions.size();
175 mVertexBuffer->setData( vertexBufferData );
176}
177
178QgsRGBPointCloud3DGeometry::QgsRGBPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
179 : QgsPointCloud3DGeometry( parent, data, byteStride )
180{
181 mPositionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
182 mPositionAttribute->setBuffer( mVertexBuffer );
183 mPositionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
184 mPositionAttribute->setVertexSize( 3 );
185 mPositionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
186 mPositionAttribute->setByteOffset( 0 );
187 mPositionAttribute->setByteStride( mByteStride );
188 addAttribute( mPositionAttribute );
189 mColorAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
190 mColorAttribute->setBuffer( mVertexBuffer );
191 mColorAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
192 mColorAttribute->setVertexSize( 3 );
193 mColorAttribute->setName( u"vertexColor"_s );
194 mColorAttribute->setByteOffset( 12 );
195 mColorAttribute->setByteStride( mByteStride );
196 addAttribute( mColorAttribute );
197 makeVertexBuffer( data );
198}
199
200void QgsRGBPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
201{
202 QByteArray vertexBufferData;
203 vertexBufferData.resize( data.positions.size() * mByteStride );
204 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
205 int idx = 0;
206 Q_ASSERT( data.positions.size() == data.colors.size() );
207 for ( int i = 0; i < data.positions.size(); ++i )
208 {
209 rawVertexArray[idx++] = data.positions.at( i ).x();
210 rawVertexArray[idx++] = data.positions.at( i ).y();
211 rawVertexArray[idx++] = data.positions.at( i ).z();
212 rawVertexArray[idx++] = data.colors.at( i ).x();
213 rawVertexArray[idx++] = data.colors.at( i ).y();
214 rawVertexArray[idx++] = data.colors.at( i ).z();
215 }
216 mVertexCount = data.positions.size();
217 mVertexBuffer->setData( vertexBufferData );
218}
219
220QgsClassificationPointCloud3DGeometry::QgsClassificationPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
221 : QgsPointCloud3DGeometry( parent, data, byteStride )
222{
223 mPositionAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
224 mPositionAttribute->setBuffer( mVertexBuffer );
225 mPositionAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
226 mPositionAttribute->setVertexSize( 3 );
227 mPositionAttribute->setName( Qt3DCore::QAttribute::defaultPositionAttributeName() );
228 mPositionAttribute->setByteOffset( 0 );
229 mPositionAttribute->setByteStride( mByteStride );
230 addAttribute( mPositionAttribute );
231 mParameterAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
232 mParameterAttribute->setBuffer( mVertexBuffer );
233 mParameterAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
234 mParameterAttribute->setVertexSize( 1 );
235 mParameterAttribute->setName( "vertexParameter" );
236 mParameterAttribute->setByteOffset( 12 );
237 mParameterAttribute->setByteStride( mByteStride );
238 addAttribute( mParameterAttribute );
239 mPointSizeAttribute->setAttributeType( Qt3DCore::QAttribute::VertexAttribute );
240 mPointSizeAttribute->setBuffer( mVertexBuffer );
241 mPointSizeAttribute->setVertexBaseType( Qt3DCore::QAttribute::Float );
242 mPointSizeAttribute->setVertexSize( 1 );
243 mPointSizeAttribute->setName( "vertexSize" );
244 mPointSizeAttribute->setByteOffset( 16 );
245 mPointSizeAttribute->setByteStride( mByteStride );
246 addAttribute( mPointSizeAttribute );
247 makeVertexBuffer( data );
248}
249
250void QgsClassificationPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
251{
252 QByteArray vertexBufferData;
253 vertexBufferData.resize( data.positions.size() * mByteStride );
254 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
255 int idx = 0;
256 Q_ASSERT( data.positions.size() == data.parameter.size() );
257 Q_ASSERT( data.positions.size() == data.pointSizes.size() );
258 for ( int i = 0; i < data.positions.size(); ++i )
259 {
260 rawVertexArray[idx++] = data.positions.at( i ).x();
261 rawVertexArray[idx++] = data.positions.at( i ).y();
262 rawVertexArray[idx++] = data.positions.at( i ).z();
263 rawVertexArray[idx++] = data.parameter.at( i );
264 rawVertexArray[idx++] = data.pointSizes.at( i );
265 }
266
267 mVertexCount = data.positions.size();
268 mVertexBuffer->setData( vertexBufferData );
269}
270
271QgsPointCloud3DSymbolHandler::QgsPointCloud3DSymbolHandler()
272{
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, const 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(
430 triangleVertices.at( 1 ) - triangleVertices.at( 0 ),
431 triangleVertices.at( 2 ) - triangleVertices.at( 0 )
432 );
433 }
434
435 // Build now normals array
436 outNormal.normals.resize( ( outNormal.positions.count() ) * sizeof( float ) * 3 );
437 float *normPtr = reinterpret_cast<float *>( outNormal.normals.data() );
438 for ( int i = 0; i < normals.size(); ++i )
439 {
440 QVector3D normal = normals.at( i );
441 normal = normal.normalized();
442
443 *normPtr++ = normal.x();
444 *normPtr++ = normal.y();
445 *normPtr++ = normal.z();
446 }
447}
448
449void QgsPointCloud3DSymbolHandler::filterTriangles( const std::vector<size_t> &triangleIndexes, const QgsPointCloud3DRenderContext &context, const QgsBox3D &box3D )
450{
451 outNormal.triangles.resize( triangleIndexes.size() * sizeof( quint32 ) );
452 quint32 *indexPtr = reinterpret_cast<quint32 *>( outNormal.triangles.data() );
453 size_t effective = 0;
454
455 bool horizontalFilter = context.symbol()->horizontalTriangleFilter();
456 bool verticalFilter = context.symbol()->verticalTriangleFilter();
457 float horizontalThreshold = context.symbol()->horizontalFilterThreshold();
458 float verticalThreshold = context.symbol()->verticalFilterThreshold();
459
460 QgsBox3D boxRelativeToChunkOrigin = box3D - outNormal.positionsOrigin;
461
462 for ( size_t i = 0; i < triangleIndexes.size(); i += 3 )
463 {
464 bool atLeastOneInBox = false;
465 bool horizontalSkip = false;
466 bool verticalSkip = false;
467 for ( size_t j = 0; j < 3; j++ )
468 {
469 QVector3D pos = outNormal.positions.at( triangleIndexes.at( i + j ) );
470 atLeastOneInBox |= boxRelativeToChunkOrigin.contains( pos.x(), pos.y(), pos.z() );
471
472 if ( verticalFilter || horizontalFilter )
473 {
474 const QVector3D pos2 = outNormal.positions.at( triangleIndexes.at( i + ( j + 1 ) % 3 ) );
475
476 if ( verticalFilter )
477 verticalSkip |= std::fabs( pos.z() - pos2.z() ) > verticalThreshold;
478
479 if ( horizontalFilter && !verticalSkip )
480 {
481 // filter only in the horizontal plan, it is a 2.5D triangulation.
482 horizontalSkip |= sqrt( std::pow( pos.x() - pos2.x(), 2 ) + std::pow( pos.y() - pos2.y(), 2 ) ) > horizontalThreshold;
483 }
484
485 if ( horizontalSkip || verticalSkip )
486 break;
487 }
488 }
489 if ( atLeastOneInBox && !horizontalSkip && !verticalSkip )
490 {
491 for ( size_t j = 0; j < 3; j++ )
492 {
493 size_t vertIndex = triangleIndexes.at( i + j );
494 *indexPtr++ = quint32( vertIndex );
495 }
496 effective++;
497 }
498 }
499
500 if ( effective != 0 )
501 {
502 outNormal.triangles.resize( effective * 3 * sizeof( quint32 ) );
503 }
504 else
505 {
506 outNormal.triangles.clear();
507 outNormal.normals.clear();
508 }
509}
510
511void QgsPointCloud3DSymbolHandler::triangulate( QgsPointCloudIndex &pc, const QgsPointCloudNodeId &n, const QgsPointCloud3DRenderContext &context, const QgsBox3D &box3D )
512{
513 if ( outNormal.positions.isEmpty() )
514 return;
515
516 // Triangulation happens here
517 std::unique_ptr<delaunator::Delaunator> triangulation;
518 try
519 {
520 std::vector<double> vertices = getVertices( pc, n, context, box3D );
521 triangulation = std::make_unique<delaunator::Delaunator>( vertices );
522 }
523 catch ( std::exception &e )
524 {
525 // something went wrong, better to retrieve initial state
526 QgsDebugMsgLevel( u"Error with triangulation"_s, 4 );
527 outNormal = PointData();
528 processNode( pc, n, context );
529 return;
530 }
531
532 const std::vector<size_t> &triangleIndexes = triangulation->triangles;
533
534 calculateNormals( triangleIndexes );
535 filterTriangles( triangleIndexes, context, box3D );
536}
537
538std::unique_ptr<QgsPointCloudBlock> QgsPointCloud3DSymbolHandler::pointCloudBlock( QgsPointCloudIndex &pc, const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request, const QgsPointCloud3DRenderContext &context )
539{
540 std::unique_ptr<QgsPointCloudBlock> block;
542 {
543 block = pc.nodeData( n, request );
544 }
546 {
547 QgsPointCloudNode node = pc.getNode( n );
548 if ( node.pointCount() < 1 )
549 return block;
550
551 bool loopAborted = false;
552 QEventLoop loop;
553 QgsPointCloudBlockRequest *req = pc.asyncNodeData( n, request );
554 QObject::connect( req, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
555 QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, [&]() {
556 loopAborted = true;
557 loop.quit();
558 } );
559 loop.exec();
560
561 if ( !loopAborted )
562 block = req->takeBlock();
563 }
564 return block;
565}
566
567//
568
569QgsSingleColorPointCloud3DSymbolHandler::QgsSingleColorPointCloud3DSymbolHandler()
570 : QgsPointCloud3DSymbolHandler()
571{
572}
573
574bool QgsSingleColorPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
575{
576 Q_UNUSED( context )
577 return true;
578}
579
580void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex &pc, const QgsPointCloudNodeId &n, const QgsPointCloud3DRenderContext &context, PointData *output )
581{
586
587 QgsPointCloudRequest request;
588 request.setAttributes( attributes );
589 request.setFilterRect( context.layerExtent() );
590 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
591 if ( !block )
592 return;
593
594 const char *ptr = block->data();
595 const int count = block->pointCount();
596 const std::size_t recordSize = block->attributes().pointRecordSize();
597 const QgsVector3D blockScale = block->scale();
598 const QgsVector3D blockOffset = block->offset();
599 const double zValueScale = context.zValueScale();
600 const double zValueOffset = context.zValueFixedOffset();
601 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
602 bool alreadyPrintedDebug = false;
603
604 if ( !output )
605 output = &outNormal;
606
607 output->positionsOrigin = originFromNodeBounds( pc, n, context );
608
609 for ( int i = 0; i < count; ++i )
610 {
611 if ( context.isCanceled() )
612 break;
613
614 const qint32 ix = *( qint32 * ) ( ptr + i * recordSize + 0 );
615 const qint32 iy = *( qint32 * ) ( ptr + i * recordSize + 4 );
616 const qint32 iz = *( qint32 * ) ( ptr + i * recordSize + 8 );
617
618 double x = blockOffset.x() + blockScale.x() * ix;
619 double y = blockOffset.y() + blockScale.y() * iy;
620 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
621 try
622 {
623 coordinateTransform.transformInPlace( x, y, z );
624 }
625 catch ( QgsCsException &e )
626 {
627 if ( !alreadyPrintedDebug )
628 {
629 QgsDebugError( u"Error transforming point coordinate"_s );
630 alreadyPrintedDebug = true;
631 }
632 }
633 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
634 output->positions.push_back( point.toVector3D() );
635 }
636}
637
638void QgsSingleColorPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
639{
640 makeEntity( parent, context, outNormal, false );
641}
642
643Qt3DCore::QGeometry *QgsSingleColorPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
644{
645 return new QgsSingleColorPointCloud3DGeometry( parent, data, byteStride );
646}
647
648QgsColorRampPointCloud3DSymbolHandler::QgsColorRampPointCloud3DSymbolHandler()
649 : QgsPointCloud3DSymbolHandler()
650{
651}
652
653bool QgsColorRampPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
654{
655 Q_UNUSED( context )
656 return true;
657}
658
659void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex &pc, const QgsPointCloudNodeId &n, const QgsPointCloud3DRenderContext &context, PointData *output )
660{
662 const int xOffset = 0;
664 const int yOffset = attributes.pointRecordSize();
666 const int zOffset = attributes.pointRecordSize();
668
669 QString attributeName;
670 bool attrIsX = false;
671 bool attrIsY = false;
672 bool attrIsZ = false;
674 int attributeOffset = 0;
675 const double zValueScale = context.zValueScale();
676 const double zValueOffset = context.zValueFixedOffset();
677 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
678 bool alreadyPrintedDebug = false;
679
680 QgsColorRampPointCloud3DSymbol *symbol = dynamic_cast<QgsColorRampPointCloud3DSymbol *>( context.symbol() );
681 if ( symbol )
682 {
683 int offset = 0;
684 const QgsPointCloudAttributeCollection collection = context.attributes();
685
686 if ( symbol->attribute() == "X"_L1 )
687 {
688 attrIsX = true;
689 }
690 else if ( symbol->attribute() == "Y"_L1 )
691 {
692 attrIsY = true;
693 }
694 else if ( symbol->attribute() == "Z"_L1 )
695 {
696 attrIsZ = true;
697 }
698 else
699 {
700 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
701 if ( attr )
702 {
703 attributeType = attr->type();
704 attributeName = attr->name();
705 attributeOffset = attributes.pointRecordSize();
706 attributes.push_back( *attr );
707 }
708 }
709 }
710
711 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
712 return;
713
714 QgsPointCloudRequest request;
715 request.setAttributes( attributes );
716 request.setFilterRect( context.layerExtent() );
717 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
718 if ( !block )
719 return;
720
721 const char *ptr = block->data();
722 const int count = block->pointCount();
723 const std::size_t recordSize = block->attributes().pointRecordSize();
724
725 const QgsVector3D blockScale = block->scale();
726 const QgsVector3D blockOffset = block->offset();
727
728 if ( !output )
729 output = &outNormal;
730
731 output->positionsOrigin = originFromNodeBounds( pc, n, context );
732
733 for ( int i = 0; i < count; ++i )
734 {
735 if ( context.isCanceled() )
736 break;
737
738 const qint32 ix = *( qint32 * ) ( ptr + i * recordSize + xOffset );
739 const qint32 iy = *( qint32 * ) ( ptr + i * recordSize + yOffset );
740 const qint32 iz = *( qint32 * ) ( ptr + i * recordSize + zOffset );
741
742 double x = blockOffset.x() + blockScale.x() * ix;
743 double y = blockOffset.y() + blockScale.y() * iy;
744 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
745 try
746 {
747 coordinateTransform.transformInPlace( x, y, z );
748 }
749 catch ( QgsCsException & )
750 {
751 if ( !alreadyPrintedDebug )
752 {
753 QgsDebugError( u"Error transforming point coordinate"_s );
754 alreadyPrintedDebug = true;
755 }
756 }
757 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
758 output->positions.push_back( point.toVector3D() );
759
760 if ( attrIsX )
761 output->parameter.push_back( x );
762 else if ( attrIsY )
763 output->parameter.push_back( y );
764 else if ( attrIsZ )
765 output->parameter.push_back( z );
766 else
767 {
768 float iParam = 0.0f;
769 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
770 output->parameter.push_back( iParam );
771 }
772 }
773}
774
775void QgsColorRampPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
776{
777 makeEntity( parent, context, outNormal, false );
778}
779
780Qt3DCore::QGeometry *QgsColorRampPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
781{
782 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
783}
784
785QgsRGBPointCloud3DSymbolHandler::QgsRGBPointCloud3DSymbolHandler()
786 : QgsPointCloud3DSymbolHandler()
787{
788}
789
790bool QgsRGBPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
791{
792 Q_UNUSED( context )
793 return true;
794}
795
796void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex &pc, const QgsPointCloudNodeId &n, const QgsPointCloud3DRenderContext &context, PointData *output )
797{
802
803 QgsRgbPointCloud3DSymbol *symbol = qgis::down_cast<QgsRgbPointCloud3DSymbol *>( context.symbol() );
804
805 // we have to get the RGB attributes using their real data types -- they aren't always short! (sometimes unsigned short)
806 int attrOffset = 0;
807
808 const int redOffset = attributes.pointRecordSize();
809 const QgsPointCloudAttribute *colorAttribute = context.attributes().find( symbol->redAttribute(), attrOffset );
810 attributes.push_back( *colorAttribute );
811 const QgsPointCloudAttribute::DataType redType = colorAttribute->type();
812
813 const int greenOffset = attributes.pointRecordSize();
814 colorAttribute = context.attributes().find( symbol->greenAttribute(), attrOffset );
815 attributes.push_back( *colorAttribute );
816 const QgsPointCloudAttribute::DataType greenType = colorAttribute->type();
817
818 const int blueOffset = attributes.pointRecordSize();
819 colorAttribute = context.attributes().find( symbol->blueAttribute(), attrOffset );
820 attributes.push_back( *colorAttribute );
821 const QgsPointCloudAttribute::DataType blueType = colorAttribute->type();
822
823 QgsPointCloudRequest request;
824 request.setAttributes( attributes );
825 request.setFilterRect( context.layerExtent() );
826 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
827 if ( !block )
828 return;
829
830 const char *ptr = block->data();
831 const int count = block->pointCount();
832 const std::size_t recordSize = block->attributes().pointRecordSize();
833
834 const QgsVector3D blockScale = block->scale();
835 const QgsVector3D blockOffset = block->offset();
836 const double zValueScale = context.zValueScale();
837 const double zValueOffset = context.zValueFixedOffset();
838 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
839 bool alreadyPrintedDebug = false;
840
841 QgsContrastEnhancement *redContrastEnhancement = symbol->redContrastEnhancement();
842 QgsContrastEnhancement *greenContrastEnhancement = symbol->greenContrastEnhancement();
843 QgsContrastEnhancement *blueContrastEnhancement = symbol->blueContrastEnhancement();
844
845 const bool useRedContrastEnhancement = redContrastEnhancement && redContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
846 const bool useBlueContrastEnhancement = blueContrastEnhancement && blueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
847 const bool useGreenContrastEnhancement = greenContrastEnhancement && greenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
848
849 if ( !output )
850 output = &outNormal;
851
852 output->positionsOrigin = originFromNodeBounds( pc, n, context );
853
854 int ir = 0;
855 int ig = 0;
856 int ib = 0;
857 for ( int i = 0; i < count; ++i )
858 {
859 if ( context.isCanceled() )
860 break;
861
862 const qint32 ix = *( qint32 * ) ( ptr + i * recordSize + 0 );
863 const qint32 iy = *( qint32 * ) ( ptr + i * recordSize + 4 );
864 const qint32 iz = *( qint32 * ) ( ptr + i * recordSize + 8 );
865 double x = blockOffset.x() + blockScale.x() * ix;
866 double y = blockOffset.y() + blockScale.y() * iy;
867 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
868 try
869 {
870 coordinateTransform.transformInPlace( x, y, z );
871 }
872 catch ( QgsCsException & )
873 {
874 if ( !alreadyPrintedDebug )
875 {
876 QgsDebugError( u"Error transforming point coordinate"_s );
877 alreadyPrintedDebug = true;
878 }
879 }
880 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
881
882 QVector3D color( 0.0f, 0.0f, 0.0f );
883
884 context.getAttribute( ptr, i * recordSize + redOffset, redType, ir );
885 context.getAttribute( ptr, i * recordSize + greenOffset, greenType, ig );
886 context.getAttribute( ptr, i * recordSize + blueOffset, blueType, ib );
887
888 //skip if red, green or blue not in displayable range
889 if ( ( useRedContrastEnhancement && !redContrastEnhancement->isValueInDisplayableRange( ir ) )
890 || ( useGreenContrastEnhancement && !greenContrastEnhancement->isValueInDisplayableRange( ig ) )
891 || ( useBlueContrastEnhancement && !blueContrastEnhancement->isValueInDisplayableRange( ib ) ) )
892 {
893 continue;
894 }
895
896 //stretch color values
897 if ( useRedContrastEnhancement )
898 {
899 ir = redContrastEnhancement->enhanceContrast( ir );
900 }
901 if ( useGreenContrastEnhancement )
902 {
903 ig = greenContrastEnhancement->enhanceContrast( ig );
904 }
905 if ( useBlueContrastEnhancement )
906 {
907 ib = blueContrastEnhancement->enhanceContrast( ib );
908 }
909
910 color.setX( ir / 255.0f );
911 color.setY( ig / 255.0f );
912 color.setZ( ib / 255.0f );
913
914 output->positions.push_back( point.toVector3D() );
915 output->colors.push_back( color );
916 }
917}
918
919void QgsRGBPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
920{
921 makeEntity( parent, context, outNormal, false );
922}
923
924Qt3DCore::QGeometry *QgsRGBPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
925{
926 return new QgsRGBPointCloud3DGeometry( parent, data, byteStride );
927}
928
929QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
930 : QgsPointCloud3DSymbolHandler()
931{
932}
933
934bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
935{
936 Q_UNUSED( context )
937 return true;
938}
939
940void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex &pc, const QgsPointCloudNodeId &n, const QgsPointCloud3DRenderContext &context, PointData *output )
941{
943 const int xOffset = 0;
945 const int yOffset = attributes.pointRecordSize();
947 const int zOffset = attributes.pointRecordSize();
949
950 QString attributeName;
951 bool attrIsX = false;
952 bool attrIsY = false;
953 bool attrIsZ = false;
955 int attributeOffset = 0;
957 if ( !symbol )
958 return;
959
960 int offset = 0;
961 const QgsPointCloudAttributeCollection collection = context.attributes();
962
963 if ( symbol->attribute() == "X"_L1 )
964 {
965 attrIsX = true;
966 }
967 else if ( symbol->attribute() == "Y"_L1 )
968 {
969 attrIsY = true;
970 }
971 else if ( symbol->attribute() == "Z"_L1 )
972 {
973 attrIsZ = true;
974 }
975 else
976 {
977 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
978 if ( attr )
979 {
980 attributeType = attr->type();
981 attributeName = attr->name();
982 attributeOffset = attributes.pointRecordSize();
983 attributes.push_back( *attr );
984 }
985 }
986
987 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
988 return;
989
990 QgsPointCloudRequest request;
991 request.setAttributes( attributes );
992 request.setFilterRect( context.layerExtent() );
993 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
994 if ( !block )
995 return;
996
997 const char *ptr = block->data();
998 const int count = block->pointCount();
999 const std::size_t recordSize = block->attributes().pointRecordSize();
1000
1001 const QgsVector3D blockScale = block->scale();
1002 const QgsVector3D blockOffset = block->offset();
1003 const double zValueScale = context.zValueScale();
1004 const double zValueOffset = context.zValueFixedOffset();
1005 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
1006 bool alreadyPrintedDebug = false;
1007
1008 QList<QgsPointCloudCategory> categoriesList = symbol->categoriesList();
1009 QVector<int> categoriesValues;
1010 QHash<int, double> categoriesPointSizes;
1011 for ( QgsPointCloudCategory &c : categoriesList )
1012 {
1013 categoriesValues.push_back( c.value() );
1014 categoriesPointSizes.insert( c.value(), c.pointSize() > 0 ? c.pointSize() : context.symbol() ? context.symbol()->pointSize()
1015 : 1.0 );
1016 }
1017
1018 if ( !output )
1019 output = &outNormal;
1020
1021 output->positionsOrigin = originFromNodeBounds( pc, n, context );
1022
1023 const QSet<int> filteredOutValues = context.getFilteredOutValues();
1024 for ( int i = 0; i < count; ++i )
1025 {
1026 if ( context.isCanceled() )
1027 break;
1028
1029 const qint32 ix = *( qint32 * ) ( ptr + i * recordSize + xOffset );
1030 const qint32 iy = *( qint32 * ) ( ptr + i * recordSize + yOffset );
1031 const qint32 iz = *( qint32 * ) ( ptr + i * recordSize + zOffset );
1032
1033 double x = blockOffset.x() + blockScale.x() * ix;
1034 double y = blockOffset.y() + blockScale.y() * iy;
1035 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
1036 try
1037 {
1038 coordinateTransform.transformInPlace( x, y, z );
1039 }
1040 catch ( QgsCsException & )
1041 {
1042 if ( !alreadyPrintedDebug )
1043 {
1044 QgsDebugError( u"Error transforming point coordinate"_s );
1045 alreadyPrintedDebug = true;
1046 }
1047 }
1048 const QgsVector3D point = QgsVector3D( x, y, z ) - output->positionsOrigin;
1049 float iParam = 0.0f;
1050 if ( attrIsX )
1051 iParam = x;
1052 else if ( attrIsY )
1053 iParam = y;
1054 else if ( attrIsZ )
1055 iParam = z;
1056 else
1057 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
1058
1059 if ( filteredOutValues.contains( ( int ) iParam ) || !categoriesValues.contains( ( int ) iParam ) )
1060 continue;
1061 output->positions.push_back( point.toVector3D() );
1062
1063 // find iParam actual index in the categories list
1064 float iParam2 = categoriesValues.indexOf( ( int ) iParam ) + 1;
1065 output->parameter.push_back( iParam2 );
1066 output->pointSizes.push_back( categoriesPointSizes.value( ( int ) iParam ) );
1067 }
1068}
1069
1070void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
1071{
1072 makeEntity( parent, context, outNormal, false );
1073}
1074
1075Qt3DCore::QGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
1076{
1077 return new QgsClassificationPointCloud3DGeometry( parent, data, byteStride );
1078}
1079
@ Local
Local means the source is a local file on the machine.
Definition qgis.h:6391
@ Remote
Remote means it's loaded through a protocol like HTTP.
Definition qgis.h:6392
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:198
bool contains(const QgsBox3D &other) const
Returns true when box contains other box.
Definition qgsbox3d.cpp:167
double width() const
Returns the width of the box.
Definition qgsbox3d.h:280
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:254
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:226
double height() const
Returns the height of the box.
Definition qgsbox3d.h:287
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.
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 void fillMaterial(QgsMaterial *material)=0SIP_SKIP
Used to fill material object with necessary QParameters (and consequently opengl uniforms).
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.
QgsPointCloudBlockRequest * asyncNodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns a handle responsible for loading a node data block.
std::unique_ptr< QgsPointCloudBlock > nodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns node data block.
QgsPointCloudNode getNode(const QgsPointCloudNodeId &id) const
Returns object for a given node.
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:52
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:54
QVector3D toVector3D() const
Converts the current object to QVector3D.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:50
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