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