QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
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
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
469std::unique_ptr<QgsPointCloudBlock> QgsPointCloud3DSymbolHandler::pointCloudBlock( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloudRequest &request, const QgsPointCloud3DRenderContext &context )
470{
471 std::unique_ptr<QgsPointCloudBlock> block;
473 {
474 block = pc->nodeData( n, request );
475 }
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->takeBlock();
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 request.setFilterRect( context.extent() );
519 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
520 if ( !block )
521 return;
522
523 const char *ptr = block->data();
524 const int count = block->pointCount();
525 const std::size_t recordSize = block->attributes().pointRecordSize();
526 const QgsVector3D blockScale = block->scale();
527 const QgsVector3D blockOffset = block->offset();
528 const double zValueScale = context.zValueScale();
529 const double zValueOffset = context.zValueFixedOffset();
530 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
531 bool alreadyPrintedDebug = false;
532
533 for ( int i = 0; i < count; ++i )
534 {
535 if ( context.isCanceled() )
536 break;
537
538 const qint32 ix = *( qint32 * )( ptr + i * recordSize + 0 );
539 const qint32 iy = *( qint32 * )( ptr + i * recordSize + 4 );
540 const qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
541
542 double x = blockOffset.x() + blockScale.x() * ix;
543 double y = blockOffset.y() + blockScale.y() * iy;
544 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
545 try
546 {
547 coordinateTransform.transformInPlace( x, y, z );
548 }
549 catch ( QgsCsException &e )
550 {
551 if ( !alreadyPrintedDebug )
552 {
553 QgsDebugError( QStringLiteral( "Error transforming point coordinate" ) );
554 alreadyPrintedDebug = true;
555 }
556 }
557 const QgsVector3D point( x, y, z );
558 const QgsVector3D p = context.map().mapToWorldCoordinates( QgsVector3D( x, y, z ) );
559 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
560 }
561}
562
563void QgsSingleColorPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
564{
565 makeEntity( parent, context, outNormal, false );
566}
567
568Qt3DQGeometry *QgsSingleColorPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
569{
570 return new QgsSingleColorPointCloud3DGeometry( parent, data, byteStride );
571}
572
573QgsColorRampPointCloud3DSymbolHandler::QgsColorRampPointCloud3DSymbolHandler()
574 : QgsPointCloud3DSymbolHandler()
575{
576
577}
578
579bool QgsColorRampPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
580{
581 Q_UNUSED( context )
582 return true;
583}
584
585void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
586{
588 const int xOffset = 0;
589 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
590 const int yOffset = attributes.pointRecordSize();
591 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
592 const int zOffset = attributes.pointRecordSize();
593 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
594
595 QString attributeName;
596 bool attrIsX = false;
597 bool attrIsY = false;
598 bool attrIsZ = false;
600 int attributeOffset = 0;
601 const double zValueScale = context.zValueScale();
602 const double zValueOffset = context.zValueFixedOffset();
603 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
604 bool alreadyPrintedDebug = false;
605
606 QgsColorRampPointCloud3DSymbol *symbol = dynamic_cast<QgsColorRampPointCloud3DSymbol *>( context.symbol() );
607 if ( symbol )
608 {
609 int offset = 0;
610 const QgsPointCloudAttributeCollection collection = context.attributes();
611
612 if ( symbol->attribute() == QLatin1String( "X" ) )
613 {
614 attrIsX = true;
615 }
616 else if ( symbol->attribute() == QLatin1String( "Y" ) )
617 {
618 attrIsY = true;
619 }
620 else if ( symbol->attribute() == QLatin1String( "Z" ) )
621 {
622 attrIsZ = true;
623 }
624 else
625 {
626 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
627 if ( attr )
628 {
629 attributeType = attr->type();
630 attributeName = attr->name();
631 attributeOffset = attributes.pointRecordSize();
632 attributes.push_back( *attr );
633 }
634 }
635 }
636
637 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
638 return;
639
640 QgsPointCloudRequest request;
641 request.setAttributes( attributes );
642 request.setFilterRect( context.extent() );
643 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
644 if ( !block )
645 return;
646
647 const char *ptr = block->data();
648 const int count = block->pointCount();
649 const std::size_t recordSize = block->attributes().pointRecordSize();
650
651 const QgsVector3D blockScale = block->scale();
652 const QgsVector3D blockOffset = block->offset();
653
654 for ( int i = 0; i < count; ++i )
655 {
656 if ( context.isCanceled() )
657 break;
658
659 const qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
660 const qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
661 const qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
662
663 double x = blockOffset.x() + blockScale.x() * ix;
664 double y = blockOffset.y() + blockScale.y() * iy;
665 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
666 try
667 {
668 coordinateTransform.transformInPlace( x, y, z );
669 }
670 catch ( QgsCsException & )
671 {
672 if ( !alreadyPrintedDebug )
673 {
674 QgsDebugError( QStringLiteral( "Error transforming point coordinate" ) );
675 alreadyPrintedDebug = true;
676 }
677 }
678 QgsVector3D point( x, y, z );
679 point = context.map().mapToWorldCoordinates( point );
680 outNormal.positions.push_back( QVector3D( point.x(), point.y(), point.z() ) );
681
682 if ( attrIsX )
683 outNormal.parameter.push_back( x );
684 else if ( attrIsY )
685 outNormal.parameter.push_back( y );
686 else if ( attrIsZ )
687 outNormal.parameter.push_back( z );
688 else
689 {
690 float iParam = 0.0f;
691 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
692 outNormal.parameter.push_back( iParam );
693 }
694 }
695}
696
697void QgsColorRampPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
698{
699 makeEntity( parent, context, outNormal, false );
700}
701
702Qt3DQGeometry *QgsColorRampPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
703{
704 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
705}
706
707QgsRGBPointCloud3DSymbolHandler::QgsRGBPointCloud3DSymbolHandler()
708 : QgsPointCloud3DSymbolHandler()
709{
710
711}
712
713bool QgsRGBPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
714{
715 Q_UNUSED( context )
716 return true;
717}
718
719void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
720{
722 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
723 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
724 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
725
726 QgsRgbPointCloud3DSymbol *symbol = dynamic_cast<QgsRgbPointCloud3DSymbol *>( context.symbol() );
727
728 // we have to get the RGB attributes using their real data types -- they aren't always short! (sometimes unsigned short)
729 int attrOffset = 0 ;
730
731 const int redOffset = attributes.pointRecordSize();
732 const QgsPointCloudAttribute *colorAttribute = context.attributes().find( symbol->redAttribute(), attrOffset );
733 attributes.push_back( *colorAttribute );
734 const QgsPointCloudAttribute::DataType redType = colorAttribute->type();
735
736 const int greenOffset = attributes.pointRecordSize();
737 colorAttribute = context.attributes().find( symbol->greenAttribute(), attrOffset );
738 attributes.push_back( *colorAttribute );
739 const QgsPointCloudAttribute::DataType greenType = colorAttribute->type();
740
741 const int blueOffset = attributes.pointRecordSize();
742 colorAttribute = context.attributes().find( symbol->blueAttribute(), attrOffset );
743 attributes.push_back( *colorAttribute );
744 const QgsPointCloudAttribute::DataType blueType = colorAttribute->type();
745
746 QgsPointCloudRequest request;
747 request.setAttributes( attributes );
748 request.setFilterRect( context.extent() );
749 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
750 if ( !block )
751 return;
752
753 const char *ptr = block->data();
754 const int count = block->pointCount();
755 const std::size_t recordSize = block->attributes().pointRecordSize();
756
757 const QgsVector3D blockScale = block->scale();
758 const QgsVector3D blockOffset = block->offset();
759 const double zValueScale = context.zValueScale();
760 const double zValueOffset = context.zValueFixedOffset();
761 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
762 bool alreadyPrintedDebug = false;
763
764 QgsContrastEnhancement *redContrastEnhancement = symbol->redContrastEnhancement();
765 QgsContrastEnhancement *greenContrastEnhancement = symbol->greenContrastEnhancement();
766 QgsContrastEnhancement *blueContrastEnhancement = symbol->blueContrastEnhancement();
767
768 const bool useRedContrastEnhancement = redContrastEnhancement && redContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
769 const bool useBlueContrastEnhancement = blueContrastEnhancement && blueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
770 const bool useGreenContrastEnhancement = greenContrastEnhancement && greenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement;
771
772 int ir = 0;
773 int ig = 0;
774 int ib = 0;
775 for ( int i = 0; i < count; ++i )
776 {
777 if ( context.isCanceled() )
778 break;
779
780 const qint32 ix = *( qint32 * )( ptr + i * recordSize + 0 );
781 const qint32 iy = *( qint32 * )( ptr + i * recordSize + 4 );
782 const qint32 iz = *( qint32 * )( ptr + i * recordSize + 8 );
783 double x = blockOffset.x() + blockScale.x() * ix;
784 double y = blockOffset.y() + blockScale.y() * iy;
785 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
786 try
787 {
788 coordinateTransform.transformInPlace( x, y, z );
789 }
790 catch ( QgsCsException & )
791 {
792 if ( !alreadyPrintedDebug )
793 {
794 QgsDebugError( QStringLiteral( "Error transforming point coordinate" ) );
795 alreadyPrintedDebug = true;
796 }
797 }
798 const QgsVector3D point( x, y, z );
799 const QgsVector3D p = context.map().mapToWorldCoordinates( point );
800
801 QVector3D color( 0.0f, 0.0f, 0.0f );
802
803 context.getAttribute( ptr, i * recordSize + redOffset, redType, ir );
804 context.getAttribute( ptr, i * recordSize + greenOffset, greenType, ig );
805 context.getAttribute( ptr, i * recordSize + blueOffset, blueType, ib );
806
807 //skip if red, green or blue not in displayable range
808 if ( ( useRedContrastEnhancement && !redContrastEnhancement->isValueInDisplayableRange( ir ) )
809 || ( useGreenContrastEnhancement && !greenContrastEnhancement->isValueInDisplayableRange( ig ) )
810 || ( useBlueContrastEnhancement && !blueContrastEnhancement->isValueInDisplayableRange( ib ) ) )
811 {
812 continue;
813 }
814
815 //stretch color values
816 if ( useRedContrastEnhancement )
817 {
818 ir = redContrastEnhancement->enhanceContrast( ir );
819 }
820 if ( useGreenContrastEnhancement )
821 {
822 ig = greenContrastEnhancement->enhanceContrast( ig );
823 }
824 if ( useBlueContrastEnhancement )
825 {
826 ib = blueContrastEnhancement->enhanceContrast( ib );
827 }
828
829 color.setX( ir / 255.0f );
830 color.setY( ig / 255.0f );
831 color.setZ( ib / 255.0f );
832
833 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
834 outNormal.colors.push_back( color );
835 }
836}
837
838void QgsRGBPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
839{
840 makeEntity( parent, context, outNormal, false );
841}
842
843Qt3DQGeometry *QgsRGBPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
844{
845 return new QgsRGBPointCloud3DGeometry( parent, data, byteStride );
846}
847
848QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
849 : QgsPointCloud3DSymbolHandler()
850{
851
852}
853
854bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
855{
856 Q_UNUSED( context )
857 return true;
858}
859
860void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
861{
863 const int xOffset = 0;
864 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
865 const int yOffset = attributes.pointRecordSize();
866 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
867 const int zOffset = attributes.pointRecordSize();
868 attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
869
870 QString attributeName;
871 bool attrIsX = false;
872 bool attrIsY = false;
873 bool attrIsZ = false;
875 int attributeOffset = 0;
877 if ( symbol )
878 {
879 int offset = 0;
880 const QgsPointCloudAttributeCollection collection = context.attributes();
881
882 if ( symbol->attribute() == QLatin1String( "X" ) )
883 {
884 attrIsX = true;
885 }
886 else if ( symbol->attribute() == QLatin1String( "Y" ) )
887 {
888 attrIsY = true;
889 }
890 else if ( symbol->attribute() == QLatin1String( "Z" ) )
891 {
892 attrIsZ = true;
893 }
894 else
895 {
896 const QgsPointCloudAttribute *attr = collection.find( symbol->attribute(), offset );
897 if ( attr )
898 {
899 attributeType = attr->type();
900 attributeName = attr->name();
901 attributeOffset = attributes.pointRecordSize();
902 attributes.push_back( *attr );
903 }
904 }
905 }
906
907 if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
908 return;
909
910 QgsPointCloudRequest request;
911 request.setAttributes( attributes );
912 request.setFilterRect( context.extent() );
913 std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
914 if ( !block )
915 return;
916
917 const char *ptr = block->data();
918 const int count = block->pointCount();
919 const std::size_t recordSize = block->attributes().pointRecordSize();
920
921 const QgsVector3D blockScale = block->scale();
922 const QgsVector3D blockOffset = block->offset();
923 const double zValueScale = context.zValueScale();
924 const double zValueOffset = context.zValueFixedOffset();
925 const QgsCoordinateTransform coordinateTransform = context.coordinateTransform();
926 bool alreadyPrintedDebug = false;
927
928 QList<QgsPointCloudCategory> categoriesList = symbol->categoriesList();
929 QVector<int> categoriesValues;
930 for ( QgsPointCloudCategory &c : categoriesList )
931 {
932 categoriesValues.push_back( c.value() );
933 }
934
935 const QSet<int> filteredOutValues = context.getFilteredOutValues();
936 for ( int i = 0; i < count; ++i )
937 {
938 if ( context.isCanceled() )
939 break;
940
941 const qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
942 const qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
943 const qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );
944
945 double x = blockOffset.x() + blockScale.x() * ix;
946 double y = blockOffset.y() + blockScale.y() * iy;
947 double z = ( blockOffset.z() + blockScale.z() * iz ) * zValueScale + zValueOffset;
948 try
949 {
950 coordinateTransform.transformInPlace( x, y, z );
951 }
952 catch ( QgsCsException & )
953 {
954 if ( !alreadyPrintedDebug )
955 {
956 QgsDebugError( QStringLiteral( "Error transforming point coordinate" ) );
957 alreadyPrintedDebug = true;
958 }
959 }
960 const QgsVector3D point( x, y, z );
961 const QgsVector3D p = context.map().mapToWorldCoordinates( point );
962 float iParam = 0.0f;
963 if ( attrIsX )
964 iParam = x;
965 else if ( attrIsY )
966 iParam = y;
967 else if ( attrIsZ )
968 iParam = z;
969 else
970 context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
971
972 if ( filteredOutValues.contains( ( int ) iParam ) ||
973 ! categoriesValues.contains( ( int ) iParam ) )
974 continue;
975 outNormal.positions.push_back( QVector3D( p.x(), p.y(), p.z() ) );
976
977 // find iParam actual index in the categories list
978 float iParam2 = categoriesValues.indexOf( ( int )iParam ) + 1;
979 outNormal.parameter.push_back( iParam2 );
980 }
981}
982
983void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
984{
985 makeEntity( parent, context, outNormal, false );
986}
987
988Qt3DQGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
989{
990 return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
991}
992
Represents a indexed point cloud node in octree.
int d() const
Returns d.
IndexedPointCloudNode parentNode() const
Returns the parent of the node.
float xMax
Definition qgsaabb.h:87
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:84
float zMax
Definition qgsaabb.h:89
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:86
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
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.
Encapsulates the render context for a 3D point cloud rendering operation.
QgsRectangle extent() const
Returns the 3D scene's extent in layer crs.
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.
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.
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.
@ Remote
Remote means it's loaded through a protocol like HTTP.
@ Local
Local means the source is a local file on the machine.
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 std::unique_ptr< QgsPointCloudBlock > nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
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.
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.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:32
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 QgsDebugError(str)
Definition qgslogger.h:38