QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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
19
23#include "qgs3dmapsettings.h"
24#include "qgspointcloudindex.h"
26#include "qgsfeedback.h"
27
28#include <Qt3DRender/QGeometryRenderer>
29#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
30#include <Qt3DRender/QAttribute>
31#include <Qt3DRender/QBuffer>
32#include <Qt3DRender/QGeometry>
33
34typedef Qt3DRender::QAttribute Qt3DQAttribute;
35typedef Qt3DRender::QBuffer Qt3DQBuffer;
36typedef Qt3DRender::QGeometry Qt3DQGeometry;
37#else
38#include <Qt3DCore/QAttribute>
39#include <Qt3DCore/QBuffer>
40#include <Qt3DCore/QGeometry>
41
42typedef Qt3DCore::QAttribute Qt3DQAttribute;
43typedef Qt3DCore::QBuffer Qt3DQBuffer;
44typedef Qt3DCore::QGeometry Qt3DQGeometry;
45#endif
46#include <Qt3DRender/QTechnique>
47#include <Qt3DRender/QShaderProgram>
48#include <Qt3DRender/QGraphicsApiFilter>
49#include <Qt3DRender/QEffect>
50#include <QPointSize>
51#include <QUrl>
52
53#include <delaunator.hpp>
54
55QgsPointCloud3DGeometry::QgsPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
56 : Qt3DQGeometry( parent )
57 , mPositionAttribute( new Qt3DQAttribute( this ) )
58 , mParameterAttribute( new Qt3DQAttribute( this ) )
59 , mColorAttribute( new Qt3DQAttribute( this ) )
60 , mTriangleIndexAttribute( new Qt3DQAttribute( this ) )
61 , mNormalsAttribute( new Qt3DQAttribute( this ) )
62 , mVertexBuffer( new Qt3DQBuffer( this ) )
63 , mByteStride( byteStride )
64{
65 if ( !data.triangles.isEmpty() )
66 {
67 mTriangleBuffer = new Qt3DQBuffer( this );
68 mTriangleIndexAttribute->setAttributeType( Qt3DQAttribute::IndexAttribute );
69 mTriangleIndexAttribute->setBuffer( mTriangleBuffer );
70 mTriangleIndexAttribute->setVertexBaseType( Qt3DQAttribute::UnsignedInt );
71 mTriangleBuffer->setData( data.triangles );
72 mTriangleIndexAttribute->setCount( data.triangles.size() / sizeof( quint32 ) );
73 addAttribute( mTriangleIndexAttribute );
74 }
75
76 if ( !data.normals.isEmpty() )
77 {
78 mNormalsBuffer = new Qt3DQBuffer( this );
79 mNormalsAttribute->setName( Qt3DQAttribute::defaultNormalAttributeName() );
80 mNormalsAttribute->setVertexBaseType( Qt3DQAttribute::Float );
81 mNormalsAttribute->setVertexSize( 3 );
82 mNormalsAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
83 mNormalsAttribute->setBuffer( mNormalsBuffer );
84 mNormalsBuffer->setData( data.normals );
85 mNormalsAttribute->setCount( data.normals.size() / ( 3 * sizeof( float ) ) );
86 addAttribute( mNormalsAttribute );
87 }
88}
89
90QgsSingleColorPointCloud3DGeometry::QgsSingleColorPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
91 : QgsPointCloud3DGeometry( parent, data, byteStride )
92{
93 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
94 mPositionAttribute->setBuffer( mVertexBuffer );
95 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
96 mPositionAttribute->setVertexSize( 3 );
97 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
98 mPositionAttribute->setByteOffset( 0 );
99 mPositionAttribute->setByteStride( mByteStride );
100 addAttribute( mPositionAttribute );
101 makeVertexBuffer( data );
102}
103
104void QgsSingleColorPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
105{
106 QByteArray vertexBufferData;
107 vertexBufferData.resize( data.positions.size() * mByteStride );
108 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
109 int idx = 0;
110 for ( int i = 0; i < data.positions.size(); ++i )
111 {
112 rawVertexArray[idx++] = data.positions.at( i ).x();
113 rawVertexArray[idx++] = data.positions.at( i ).y();
114 rawVertexArray[idx++] = data.positions.at( i ).z();
115 }
116
117 mVertexCount = data.positions.size();
118 mVertexBuffer->setData( vertexBufferData );
119}
120
121QgsColorRampPointCloud3DGeometry::QgsColorRampPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
122 : QgsPointCloud3DGeometry( parent, data, byteStride )
123{
124 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
125 mPositionAttribute->setBuffer( mVertexBuffer );
126 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
127 mPositionAttribute->setVertexSize( 3 );
128 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
129 mPositionAttribute->setByteOffset( 0 );
130 mPositionAttribute->setByteStride( mByteStride );
131 addAttribute( mPositionAttribute );
132 mParameterAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
133 mParameterAttribute->setBuffer( mVertexBuffer );
134 mParameterAttribute->setVertexBaseType( Qt3DQAttribute::Float );
135 mParameterAttribute->setVertexSize( 1 );
136 mParameterAttribute->setName( "vertexParameter" );
137 mParameterAttribute->setByteOffset( 12 );
138 mParameterAttribute->setByteStride( mByteStride );
139 addAttribute( mParameterAttribute );
140 makeVertexBuffer( data );
141}
142
143void QgsColorRampPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
144{
145 QByteArray vertexBufferData;
146 vertexBufferData.resize( data.positions.size() * mByteStride );
147 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
148 int idx = 0;
149 Q_ASSERT( data.positions.size() == data.parameter.size() );
150 for ( int i = 0; i < data.positions.size(); ++i )
151 {
152 rawVertexArray[idx++] = data.positions.at( i ).x();
153 rawVertexArray[idx++] = data.positions.at( i ).y();
154 rawVertexArray[idx++] = data.positions.at( i ).z();
155 rawVertexArray[idx++] = data.parameter.at( i );
156 }
157
158 mVertexCount = data.positions.size();
159 mVertexBuffer->setData( vertexBufferData );
160}
161
162QgsRGBPointCloud3DGeometry::QgsRGBPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
163 : QgsPointCloud3DGeometry( parent, data, byteStride )
164{
165 mPositionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
166 mPositionAttribute->setBuffer( mVertexBuffer );
167 mPositionAttribute->setVertexBaseType( Qt3DQAttribute::Float );
168 mPositionAttribute->setVertexSize( 3 );
169 mPositionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() );
170 mPositionAttribute->setByteOffset( 0 );
171 mPositionAttribute->setByteStride( mByteStride );
172 addAttribute( mPositionAttribute );
173 mColorAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute );
174 mColorAttribute->setBuffer( mVertexBuffer );
175 mColorAttribute->setVertexBaseType( Qt3DQAttribute::Float );
176 mColorAttribute->setVertexSize( 3 );
177 mColorAttribute->setName( QStringLiteral( "vertexColor" ) );
178 mColorAttribute->setByteOffset( 12 );
179 mColorAttribute->setByteStride( mByteStride );
180 addAttribute( mColorAttribute );
181 makeVertexBuffer( data );
182}
183
184void QgsRGBPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
185{
186 QByteArray vertexBufferData;
187 vertexBufferData.resize( data.positions.size() * mByteStride );
188 float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
189 int idx = 0;
190 Q_ASSERT( data.positions.size() == data.colors.size() );
191 for ( int i = 0; i < data.positions.size(); ++i )
192 {
193 rawVertexArray[idx++] = data.positions.at( i ).x();
194 rawVertexArray[idx++] = data.positions.at( i ).y();
195 rawVertexArray[idx++] = data.positions.at( i ).z();
196 rawVertexArray[idx++] = data.colors.at( i ).x();
197 rawVertexArray[idx++] = data.colors.at( i ).y();
198 rawVertexArray[idx++] = data.colors.at( i ).z();
199 }
200 mVertexCount = data.positions.size();
201 mVertexBuffer->setData( vertexBufferData );
202}
203
204QgsPointCloud3DSymbolHandler::QgsPointCloud3DSymbolHandler()
205{
206}
207
208
209void QgsPointCloud3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context, const QgsPointCloud3DSymbolHandler::PointData &out, bool selected )
210{
211 Q_UNUSED( selected )
212
213 if ( out.positions.empty() )
214 return;
215
216 // Geometry
217 Qt3DQGeometry *geom = makeGeometry( parent, out, context.symbol()->byteStride() );
218 Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer;
219 if ( context.symbol()->renderAsTriangles() && ! out.triangles.isEmpty() )
220 {
221 gr->setPrimitiveType( Qt3DRender::QGeometryRenderer::Triangles );
222 gr->setVertexCount( out.triangles.size() / sizeof( quint32 ) );
223 }
224 else
225 {
226 gr->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
227 gr->setVertexCount( out.positions.count() );
228 }
229 gr->setGeometry( geom );
230
231 // Transform
232 Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
233
234 // Material
235 Qt3DRender::QMaterial *mat = new Qt3DRender::QMaterial;
236 if ( context.symbol() )
237 context.symbol()->fillMaterial( mat );
238
239 Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram( mat );
240 shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/pointcloud.vert" ) ) ) );
241 shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/pointcloud.frag" ) ) ) );
242
243 Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass( mat );
244 renderPass->setShaderProgram( shaderProgram );
245
246 if ( out.triangles.isEmpty() )
247 {
248 Qt3DRender::QPointSize *pointSize = new Qt3DRender::QPointSize( renderPass );
249 pointSize->setSizeMode( Qt3DRender::QPointSize::Programmable ); // supported since OpenGL 3.2
250 pointSize->setValue( context.symbol() ? context.symbol()->pointSize() : 1.0f );
251 renderPass->addRenderState( pointSize );
252 }
253
254 // without this filter the default forward renderer would not render this
255 Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
256 filterKey->setName( QStringLiteral( "renderingStyle" ) );
257 filterKey->setValue( "forward" );
258
259 Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
260 technique->addRenderPass( renderPass );
261 technique->addFilterKey( filterKey );
262 technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
263 technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
264 technique->graphicsApiFilter()->setMajorVersion( 3 );
265 technique->graphicsApiFilter()->setMinorVersion( 1 );
266 technique->addParameter( new Qt3DRender::QParameter( "triangulate", !out.triangles.isEmpty() ) );
267
268 Qt3DRender::QEffect *eff = new Qt3DRender::QEffect;
269 eff->addTechnique( technique );
270 mat->setEffect( eff );
271
272 // All together
273 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
274 entity->addComponent( gr );
275 entity->addComponent( tr );
276 entity->addComponent( mat );
277 entity->setParent( parent );
278 // cppcheck wrongly believes entity will leak
279 // cppcheck-suppress memleak
280}
281
282
283std::vector<double> QgsPointCloud3DSymbolHandler::getVertices( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, const QgsAABB &bbox )
284{
285
286 bool hasColorData = !outNormal.colors.empty();
287 bool hasParameterData = !outNormal.parameter.empty();
288
289 // first, get the points of the concerned node
290 std::vector<double> vertices( outNormal.positions.size() * 2 );
291 size_t idx = 0;
292 for ( int i = 0; i < outNormal.positions.size(); ++i )
293 {
294 vertices[idx++] = outNormal.positions.at( i ).x();
295 vertices[idx++] = outNormal.positions.at( i ).z();
296 }
297
298 // next, we also need all points of all parents nodes to make the triangulation (also external points)
299 IndexedPointCloudNode parentNode = n.parentNode();
300
301 int properPointsCount = outNormal.positions.count();
302 while ( parentNode.d() >= 0 )
303 {
304 processNode( pc, parentNode, context );
305 parentNode = parentNode.parentNode();
306 }
307
308 PointData filteredExtraPointData;
309
310 double span = pc->span();
311 //factor to take account of the density of the point to calculate extension of the bounding box
312 // with a usual value span = 128, bounding box is extended by 12.5 % on each side.
313 double extraBoxFactor = 16 / span;
314 double extraX = extraBoxFactor * bbox.xExtent();
315 double extraZ = extraBoxFactor * bbox.zExtent();
316
317 // We keep all points in vertical direction to avoid odd triangulation if points are isolated on top
318 const QgsAABB extendedBBox( bbox.xMin - extraX, -std::numeric_limits<float>::max(), bbox.zMin - extraZ, bbox.xMax + extraX, std::numeric_limits<float>::max(), bbox.zMax + extraZ );
319
320 for ( int i = properPointsCount; i < outNormal.positions.count(); ++i )
321 {
322 const QVector3D pos = outNormal.positions.at( i );
323 if ( extendedBBox.intersects( pos.x(), pos.y(), pos.z() ) )
324 {
325 filteredExtraPointData.positions.append( pos );
326 vertices.push_back( pos.x() );
327 vertices.push_back( pos.z() );
328
329 if ( hasColorData )
330 filteredExtraPointData.colors.append( outNormal.colors.at( i ) );
331 if ( hasParameterData )
332 filteredExtraPointData.parameter.append( outNormal.parameter.at( i ) );
333 }
334 }
335
336 outNormal.positions.resize( properPointsCount );
337 if ( hasColorData )
338 outNormal.colors.resize( properPointsCount );
339 if ( hasParameterData )
340 outNormal.parameter.resize( properPointsCount );
341
342 outNormal.positions.append( filteredExtraPointData.positions );
343 outNormal.colors.append( filteredExtraPointData.colors );
344 outNormal.parameter.append( filteredExtraPointData.parameter );
345
346 return vertices;
347}
348
349void QgsPointCloud3DSymbolHandler::calculateNormals( const std::vector<size_t> &triangles )
350{
351 QVector<QVector3D> normals( outNormal.positions.count(), QVector3D( 0.0, 0.0, 0.0 ) );
352 for ( size_t i = 0; i < triangles.size(); i += 3 )
353 {
354 QVector<QVector3D> triangleVertices( 3 );
355 for ( size_t j = 0; j < 3; ++j )
356 {
357 size_t vertIndex = triangles.at( i + j );
358 triangleVertices[j] = outNormal.positions.at( vertIndex );
359 }
360 //calculate normals
361 for ( size_t j = 0; j < 3; ++j )
362 normals[triangles.at( i + j )] += QVector3D::crossProduct(
363 triangleVertices.at( 1 ) - triangleVertices.at( 0 ),
364 triangleVertices.at( 2 ) - triangleVertices.at( 0 ) );
365 }
366
367 // Build now normals array
368 outNormal.normals.resize( ( outNormal.positions.count() ) * sizeof( float ) * 3 );
369 float *normPtr = reinterpret_cast<float *>( outNormal.normals.data() );
370 for ( int i = 0; i < normals.size(); ++i )
371 {
372 QVector3D normal = normals.at( i );
373 normal = normal.normalized();
374
375 *normPtr++ = normal.x();
376 *normPtr++ = normal.y();
377 *normPtr++ = normal.z();
378 }
379}
380
381void QgsPointCloud3DSymbolHandler::filterTriangles( const std::vector<size_t> &triangleIndexes, const QgsPointCloud3DRenderContext &context, const QgsAABB &bbox )
382{
383 outNormal.triangles.resize( triangleIndexes.size() * sizeof( quint32 ) );
384 quint32 *indexPtr = reinterpret_cast<quint32 *>( outNormal.triangles.data() );
385 size_t effective = 0;
386
387 bool horizontalFilter = context.symbol()->horizontalTriangleFilter();
388 bool verticalFilter = context.symbol()->verticalTriangleFilter();
389 float horizontalThreshold = context.symbol()->horizontalFilterThreshold();
390 float verticalThreshold = context.symbol()->verticalFilterThreshold();
391
392 for ( size_t i = 0; i < triangleIndexes.size(); i += 3 )
393 {
394 bool atLeastOneInBox = false;
395 bool horizontalSkip = false;
396 bool verticalSkip = false;
397 for ( size_t j = 0; j < 3; j++ )
398 {
399 QVector3D pos = outNormal.positions.at( triangleIndexes.at( i + j ) );
400 atLeastOneInBox |= bbox.intersects( pos.x(), pos.y(), pos.z() );
401
402 if ( verticalFilter || horizontalFilter )
403 {
404 const QVector3D pos2 = outNormal.positions.at( triangleIndexes.at( i + ( j + 1 ) % 3 ) );
405
406 if ( verticalFilter )
407 verticalSkip |= std::fabs( pos.y() - pos2.y() ) > verticalThreshold;
408
409 if ( horizontalFilter && ! verticalSkip )
410 {
411 // filter only in the horizontal plan, it is a 2.5D triangulation.
412 horizontalSkip |= sqrt( std::pow( pos.x() - pos2.x(), 2 ) +
413 std::pow( pos.z() - pos2.z(), 2 ) ) > horizontalThreshold;
414 }
415
416 if ( horizontalSkip || verticalSkip )
417 break;
418 }
419 }
420 if ( atLeastOneInBox && !horizontalSkip && !verticalSkip )
421 {
422 for ( size_t j = 0; j < 3; j++ )
423 {
424 size_t vertIndex = triangleIndexes.at( i + j );
425 *indexPtr++ = quint32( vertIndex );
426 }
427 effective++;
428 }
429 }
430
431 if ( effective != 0 )
432 {
433 outNormal.triangles.resize( effective * 3 * sizeof( quint32 ) );
434 }
435 else
436 {
437 outNormal.triangles.clear();
438 outNormal.normals.clear();
439 }
440}
441
442void QgsPointCloud3DSymbolHandler::triangulate( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context, const QgsAABB &bbox )
443{
444 if ( outNormal.positions.isEmpty() )
445 return;
446
447 // Triangulation happens here
448 std::unique_ptr<delaunator::Delaunator> triangulation;
449 try
450 {
451 std::vector<double> vertices = getVertices( pc, n, context, bbox );
452 triangulation.reset( new delaunator::Delaunator( vertices ) );
453 }
454 catch ( std::exception &e )
455 {
456 // something went wrong, better to retrieve initial state
457 QgsDebugMsgLevel( QStringLiteral( "Error with triangulation" ), 4 );
458 outNormal = PointData();
459 processNode( pc, n, context );
460 return;
461 }
462
463 const std::vector<size_t> &triangleIndexes = triangulation->triangles;
464
465 calculateNormals( triangleIndexes );
466 filterTriangles( triangleIndexes, context, bbox );
467}
468
469QgsPointCloudBlock *QgsPointCloud3DSymbolHandler::pointCloudBlock( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloudRequest &request, const QgsPointCloud3DRenderContext &context )
470{
471 QgsPointCloudBlock *block = nullptr;
472 if ( pc->accessType() == QgsPointCloudIndex::AccessType::Local )
473 {
474 block = pc->nodeData( n, request );
475 }
476 else if ( pc->accessType() == QgsPointCloudIndex::AccessType::Remote )
477 {
478 bool loopAborted = false;
479 QEventLoop loop;
480 QgsPointCloudBlockRequest *req = pc->asyncNodeData( n, request );
481 QObject::connect( req, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
482 QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, [ & ]()
483 {
484 loopAborted = true;
485 loop.quit();
486 } );
487 loop.exec();
488
489 if ( !loopAborted )
490 block = req->block();
491 }
492 return block;
493}
494
495//
496
497QgsSingleColorPointCloud3DSymbolHandler::QgsSingleColorPointCloud3DSymbolHandler()
498 : QgsPointCloud3DSymbolHandler()
499{
500
501}
502
503bool QgsSingleColorPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
504{
505 Q_UNUSED( context )
506 return true;
507}
508
509void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
510{
512 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
513 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
514 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
515
516 QgsPointCloudRequest request;
517 request.setAttributes( attributes );
518 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
519 if ( !block )
520 return;
521
522 const char *ptr = block->data();
523 const int count = block->pointCount();
524 const std::size_t recordSize = block->attributes().pointRecordSize();
525 const QgsVector3D blockScale = block->scale();
526 const QgsVector3D blockOffset = block->offset();
527 const double zValueScale = context.zValueScale();
528 const double zValueOffset = context.zValueFixedOffset();
529 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
530 bool alreadyPrintedDebug = false;
531
532 for ( int i = 0; i < count; ++i )
533 {
534 if ( context.isCanceled() )
535 break;
536
537 const qint32 ix = *( qint32 * )( ptr + i * recordSize + 0 );
538 const qint32 iy = *( qint32 * )( ptr + i * recordSize + 4 );
539 const qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
540
541 double x = blockOffset.x() + blockScale.x() * ix;
542 double y = blockOffset.y() + blockScale.y() * iy;
543 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
544 try
545 {
546 coordinateTransform.transformInPlace( x, y, z );
547 }
548 catch ( QgsCsException &e )
549 {
550 if ( !alreadyPrintedDebug )
551 {
552 QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
553 alreadyPrintedDebug = true;
554 }
555 }
556 const QgsVector3D point( x, y, z );
557 const QgsVector3D p = context.map().mapToWorldCoordinates( QgsVector3D( x, y, z ) );
558 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
559 }
560}
561
562void QgsSingleColorPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
563{
564 makeEntity( parent, context, outNormal, false );
565}
566
567Qt3DQGeometry *QgsSingleColorPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
568{
569 return new QgsSingleColorPointCloud3DGeometry( parent, data, byteStride );
570}
571
572QgsColorRampPointCloud3DSymbolHandler::QgsColorRampPointCloud3DSymbolHandler()
573 : QgsPointCloud3DSymbolHandler()
574{
575
576}
577
578bool QgsColorRampPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
579{
580 Q_UNUSED( context )
581 return true;
582}
583
584void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
585{
587 const int xOffset = 0;
588 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
589 const int yOffset = attributes.pointRecordSize();
590 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
591 const int zOffset = attributes.pointRecordSize();
592 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
593
594 QString attributeName;
595 bool attrIsX = false;
596 bool attrIsY = false;
597 bool attrIsZ = false;
599 int attributeOffset = 0;
600 const double zValueScale = context.zValueScale();
601 const double zValueOffset = context.zValueFixedOffset();
602 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
603 bool alreadyPrintedDebug = false;
604
605 QgsColorRampPointCloud3DSymbol *symbol = dynamic_cast<QgsColorRampPointCloud3DSymbol *>( context.symbol() );
606 if ( symbol )
607 {
608 int offset = 0;
609 const QgsPointCloudAttributeCollection collection = context.attributes();
610
611 if ( symbol->attribute() == QLatin1String( "X" ) )
612 {
613 attrIsX = true;
614 }
615 else if ( symbol->attribute() == QLatin1String( "Y" ) )
616 {
617 attrIsY = true;
618 }
619 else if ( symbol->attribute() == QLatin1String( "Z" ) )
620 {
621 attrIsZ = true;
622 }
623 else
624 {
625 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
626 if ( attr )
627 {
628 attributeType = attr->type();
629 attributeName = attr->name();
630 attributeOffset = attributes.pointRecordSize();
631 attributes.push_back( *attr );
632 }
633 }
634 }
635
636 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
637 return;
638
639 QgsPointCloudRequest request;
640 request.setAttributes( attributes );
641 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
642 if ( !block )
643 return;
644
645 const char *ptr = block->data();
646 const int count = block->pointCount();
647 const std::size_t recordSize = block->attributes().pointRecordSize();
648
649 const QgsVector3D blockScale = block->scale();
650 const QgsVector3D blockOffset = block->offset();
651
652 for ( int i = 0; i < count; ++i )
653 {
654 if ( context.isCanceled() )
655 break;
656
657 const qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
658 const qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
659 const qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
660
661 double x = blockOffset.x() + blockScale.x() * ix;
662 double y = blockOffset.y() + blockScale.y() * iy;
663 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
664 try
665 {
666 coordinateTransform.transformInPlace( x, y, z );
667 }
668 catch ( QgsCsException & )
669 {
670 if ( !alreadyPrintedDebug )
671 {
672 QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
673 alreadyPrintedDebug = true;
674 }
675 }
676 QgsVector3D point( x, y, z );
677 point = context.map().mapToWorldCoordinates( point );
678 outNormal.positions.push_back( QVector3D( point.x(), point.y(), point.z() ) );
679
680 if ( attrIsX )
681 outNormal.parameter.push_back( x );
682 else if ( attrIsY )
683 outNormal.parameter.push_back( y );
684 else if ( attrIsZ )
685 outNormal.parameter.push_back( z );
686 else
687 {
688 float iParam = 0.0f;
689 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
690 outNormal.parameter.push_back( iParam );
691 }
692 }
693}
694
695void QgsColorRampPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
696{
697 makeEntity( parent, context, outNormal, false );
698}
699
700Qt3DQGeometry *QgsColorRampPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
701{
702 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
703}
704
705QgsRGBPointCloud3DSymbolHandler::QgsRGBPointCloud3DSymbolHandler()
706 : QgsPointCloud3DSymbolHandler()
707{
708
709}
710
711bool QgsRGBPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
712{
713 Q_UNUSED( context )
714 return true;
715}
716
717void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
718{
720 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
721 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
722 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
723
724 QgsRgbPointCloud3DSymbol *symbol = dynamic_cast<QgsRgbPointCloud3DSymbol *>( context.symbol() );
725
726 // we have to get the RGB attributes using their real data types -- they aren't always short! (sometimes unsigned short)
727 int attrOffset = 0 ;
728
729 const int redOffset = attributes.pointRecordSize();
730 const QgsPointCloudAttribute *colorAttribute = context.attributes().find( symbol->redAttribute(), attrOffset );
731 attributes.push_back( *colorAttribute );
732 const QgsPointCloudAttribute::DataType redType = colorAttribute->type();
733
734 const int greenOffset = attributes.pointRecordSize();
735 colorAttribute = context.attributes().find( symbol->greenAttribute(), attrOffset );
736 attributes.push_back( *colorAttribute );
737 const QgsPointCloudAttribute::DataType greenType = colorAttribute->type();
738
739 const int blueOffset = attributes.pointRecordSize();
740 colorAttribute = context.attributes().find( symbol->blueAttribute(), attrOffset );
741 attributes.push_back( *colorAttribute );
742 const QgsPointCloudAttribute::DataType blueType = colorAttribute->type();
743
744 QgsPointCloudRequest request;
745 request.setAttributes( attributes );
746 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
747 if ( !block )
748 return;
749
750 const char *ptr = block->data();
751 const int count = block->pointCount();
752 const std::size_t recordSize = block->attributes().pointRecordSize();
753
754 const QgsVector3D blockScale = block->scale();
755 const QgsVector3D blockOffset = block->offset();
756 const double zValueScale = context.zValueScale();
757 const double zValueOffset = context.zValueFixedOffset();
758 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
759 bool alreadyPrintedDebug = false;
760
761 QgsContrastEnhancement *redContrastEnhancement = symbol->redContrastEnhancement();
762 QgsContrastEnhancement *greenContrastEnhancement = symbol->greenContrastEnhancement();
763 QgsContrastEnhancement *blueContrastEnhancement = symbol->blueContrastEnhancement();
764
765 const bool useRedContrastEnhancement = redContrastEnhancement && redContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
766 const bool useBlueContrastEnhancement = blueContrastEnhancement && blueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
767 const bool useGreenContrastEnhancement = greenContrastEnhancement && greenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
768
769 int ir = 0;
770 int ig = 0;
771 int ib = 0;
772 for ( int i = 0; i < count; ++i )
773 {
774 if ( context.isCanceled() )
775 break;
776
777 const qint32 ix = *( qint32 * )( ptr + i * recordSize + 0 );
778 const qint32 iy = *( qint32 * )( ptr + i * recordSize + 4 );
779 const qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
780 double x = blockOffset.x() + blockScale.x() * ix;
781 double y = blockOffset.y() + blockScale.y() * iy;
782 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
783 try
784 {
785 coordinateTransform.transformInPlace( x, y, z );
786 }
787 catch ( QgsCsException & )
788 {
789 if ( !alreadyPrintedDebug )
790 {
791 QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
792 alreadyPrintedDebug = true;
793 }
794 }
795 const QgsVector3D point( x, y, z );
796 const QgsVector3D p = context.map().mapToWorldCoordinates( point );
797
798 QVector3D color( 0.0f, 0.0f, 0.0f );
799
800 context.getAttribute( ptr, i * recordSize + redOffset, redType, ir );
801 context.getAttribute( ptr, i * recordSize + greenOffset, greenType, ig );
802 context.getAttribute( ptr, i * recordSize + blueOffset, blueType, ib );
803
804 //skip if red, green or blue not in displayable range
805 if ( ( useRedContrastEnhancement && !redContrastEnhancement->isValueInDisplayableRange( ir ) )
806 || ( useGreenContrastEnhancement && !greenContrastEnhancement->isValueInDisplayableRange( ig ) )
807 || ( useBlueContrastEnhancement && !blueContrastEnhancement->isValueInDisplayableRange( ib ) ) )
808 {
809 continue;
810 }
811
812 //stretch color values
813 if ( useRedContrastEnhancement )
814 {
815 ir = redContrastEnhancement->enhanceContrast( ir );
816 }
817 if ( useGreenContrastEnhancement )
818 {
819 ig = greenContrastEnhancement->enhanceContrast( ig );
820 }
821 if ( useBlueContrastEnhancement )
822 {
823 ib = blueContrastEnhancement->enhanceContrast( ib );
824 }
825
826 color.setX( ir / 255.0f );
827 color.setY( ig / 255.0f );
828 color.setZ( ib / 255.0f );
829
830 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
831 outNormal.colors.push_back( color );
832 }
833}
834
835void QgsRGBPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
836{
837 makeEntity( parent, context, outNormal, false );
838}
839
840Qt3DQGeometry *QgsRGBPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
841{
842 return new QgsRGBPointCloud3DGeometry( parent, data, byteStride );
843}
844
845QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
846 : QgsPointCloud3DSymbolHandler()
847{
848
849}
850
851bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
852{
853 Q_UNUSED( context )
854 return true;
855}
856
857void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
858{
860 const int xOffset = 0;
861 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
862 const int yOffset = attributes.pointRecordSize();
863 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
864 const int zOffset = attributes.pointRecordSize();
865 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
866
867 QString attributeName;
868 bool attrIsX = false;
869 bool attrIsY = false;
870 bool attrIsZ = false;
872 int attributeOffset = 0;
874 if ( symbol )
875 {
876 int offset = 0;
877 const QgsPointCloudAttributeCollection collection = context.attributes();
878
879 if ( symbol->attribute() == QLatin1String( "X" ) )
880 {
881 attrIsX = true;
882 }
883 else if ( symbol->attribute() == QLatin1String( "Y" ) )
884 {
885 attrIsY = true;
886 }
887 else if ( symbol->attribute() == QLatin1String( "Z" ) )
888 {
889 attrIsZ = true;
890 }
891 else
892 {
893 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
894 if ( attr )
895 {
896 attributeType = attr->type();
897 attributeName = attr->name();
898 attributeOffset = attributes.pointRecordSize();
899 attributes.push_back( *attr );
900 }
901 }
902 }
903
904 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
905 return;
906
907 QgsPointCloudRequest request;
908 request.setAttributes( attributes );
909 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
910 if ( !block )
911 return;
912
913 const char *ptr = block->data();
914 const int count = block->pointCount();
915 const std::size_t recordSize = block->attributes().pointRecordSize();
916
917 const QgsVector3D blockScale = block->scale();
918 const QgsVector3D blockOffset = block->offset();
919 const double zValueScale = context.zValueScale();
920 const double zValueOffset = context.zValueFixedOffset();
921 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
922 bool alreadyPrintedDebug = false;
923
924 QList<QgsPointCloudCategory> categoriesList = symbol->categoriesList();
925 QVector<int> categoriesValues;
926 for ( QgsPointCloudCategory &c : categoriesList )
927 {
928 categoriesValues.push_back( c.value() );
929 }
930
931 const QSet<int> filteredOutValues = context.getFilteredOutValues();
932 for ( int i = 0; i < count; ++i )
933 {
934 if ( context.isCanceled() )
935 break;
936
937 const qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
938 const qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
939 const qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
940
941 double x = blockOffset.x() + blockScale.x() * ix;
942 double y = blockOffset.y() + blockScale.y() * iy;
943 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
944 try
945 {
946 coordinateTransform.transformInPlace( x, y, z );
947 }
948 catch ( QgsCsException & )
949 {
950 if ( !alreadyPrintedDebug )
951 {
952 QgsDebugMsg( QStringLiteral( "Error transforming point coordinate" ) );
953 alreadyPrintedDebug = true;
954 }
955 }
956 const QgsVector3D point( x, y, z );
957 const QgsVector3D p = context.map().mapToWorldCoordinates( point );
958 float iParam = 0.0f;
959 if ( attrIsX )
960 iParam = x;
961 else if ( attrIsY )
962 iParam = y;
963 else if ( attrIsZ )
964 iParam = z;
965 else
966 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
967
968 if ( filteredOutValues.contains( ( int ) iParam ) ||
969 ! categoriesValues.contains( ( int ) iParam ) )
970 continue;
971 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
972
973 // find iParam actual index in the categories list
974 float iParam2 = categoriesValues.indexOf( ( int )iParam ) + 1;
975 outNormal.parameter.push_back( iParam2 );
976 }
977}
978
979void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
980{
981 makeEntity( parent, context, outNormal, false );
982}
983
984Qt3DQGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
985{
986 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
987}
988
Represents a indexed point cloud node in octree.
int d() const
Returns d.
IndexedPointCloudNode parentNode() const
Returns the parent of the node.
3
Definition: qgsaabb.h:34
float xMax
Definition: qgsaabb.h:84
float xExtent() const
Returns box width in X axis.
Definition: qgsaabb.h:43
float zExtent() const
Returns box width in Z axis.
Definition: qgsaabb.h:47
float xMin
Definition: qgsaabb.h:81
float zMax
Definition: qgsaabb.h:86
bool intersects(const QgsAABB &other) const
Determines whether the box intersects some other axis aligned box.
Definition: qgsaabb.cpp:35
float zMin
Definition: qgsaabb.h:83
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.
QString attribute() const
Returns the attribute used to select the color of the point cloud.
Manipulates raster or point cloud pixel values so that they enhanceContrast or clip into a specified ...
@ 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
Class for doing transforms between two map coordinate systems.
void transformInPlace(double &x, double &y, double &z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
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.
Definition: qgsexception.h:66
void canceled()
Internal routines can connect to this signal if they use event loop.
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.
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 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.
virtual void fillMaterial(Qt3DRender::QMaterial *material)=0SIP_SKIP
Used to fill material object with necessary QParameters (and consequently opengl uniforms)
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.
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.
QgsPointCloudBlock * block()
Returns the requested block.
void finished()
Emitted when the request processing has finished.
Base class for storing raw data from point cloud nodes.
QgsVector3D scale() const
Returns the custom scale of the block.
const char * data() const
Returns raw pointer to data.
QgsPointCloudAttributeCollection attributes() const
Returns the attributes that are stored in the data block, along with their size.
int pointCount() const
Returns number of points that are stored in the block.
QgsVector3D offset() const
Returns the custom offset of the block.
Represents an individual category (class) from a QgsPointCloudClassifiedRenderer.
Represents a indexed point clouds data in octree.
int span() const
Returns the number of points in one direction in a single node.
virtual QgsPointCloudBlockRequest * asyncNodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns a handle responsible for loading a node data block.
virtual AccessType accessType() const =0
Returns the access type of the data If the access type is Remote, data will be fetched from an HTTP s...
virtual QgsPointCloudBlock * nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
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.
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
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
Qt3DCore::QAttribute Qt3DQAttribute
Definition: qgs3daxis.cpp:28
Qt3DCore::QBuffer Qt3DQBuffer
Definition: qgs3daxis.cpp:30
Qt3DCore::QGeometry Qt3DQGeometry
Definition: qgs3daxis.cpp:29
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38