QGIS API Documentation 3.29.0-Master (19d7edcfed)
qgs3dutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgs3dutils.cpp
3 --------------------------------------
4 Date : July 2017
5 Copyright : (C) 2017 by Martin Dobias
6 Email : wonder dot sk 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
16#include "qgs3dutils.h"
17
18#include "qgslinestring.h"
19#include "qgspolygon.h"
20#include "qgsfeaturerequest.h"
21#include "qgsfeatureiterator.h"
22#include "qgsfeature.h"
23#include "qgsabstractgeometry.h"
24#include "qgsvectorlayer.h"
26#include "qgsfeedback.h"
27#include "qgsexpression.h"
28#include "qgsexpressionutils.h"
30
31#include "qgs3dmapscene.h"
32#include "qgsabstract3dengine.h"
33#include "qgsterraingenerator.h"
34#include "qgscameracontroller.h"
35
36#include "qgsline3dsymbol.h"
37#include "qgspoint3dsymbol.h"
38#include "qgspolygon3dsymbol.h"
39
46
47#include <QtMath>
48#include <Qt3DExtras/QPhongMaterial>
49#include <Qt3DRender/QRenderSettings>
50
52{
53 QImage resImage;
54 QEventLoop evLoop;
55
56 // We need to change render policy to RenderPolicy::Always, since otherwise render capture node won't work
57 engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::Always );
58
59 auto requestImageFcn = [&engine, scene]
60 {
61 if ( scene->sceneState() == Qgs3DMapScene::Ready )
62 {
63 engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::OnDemand );
64 engine.requestCaptureImage();
65 }
66 };
67
68 auto saveImageFcn = [&evLoop, &resImage]( const QImage & img )
69 {
70 resImage = img;
71 evLoop.quit();
72 };
73
74 const QMetaObject::Connection conn1 = QObject::connect( &engine, &QgsAbstract3DEngine::imageCaptured, saveImageFcn );
75 QMetaObject::Connection conn2;
76
77 if ( scene->sceneState() == Qgs3DMapScene::Ready )
78 {
79 requestImageFcn();
80 }
81 else
82 {
83 // first wait until scene is loaded
84 conn2 = QObject::connect( scene, &Qgs3DMapScene::sceneStateChanged, requestImageFcn );
85 }
86
87 evLoop.exec();
88
89 QObject::disconnect( conn1 );
90 if ( conn2 )
91 QObject::disconnect( conn2 );
92
93 engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::OnDemand );
94 return resImage;
95}
96
98{
99 QImage resImage;
100 QEventLoop evLoop;
101
102 // We need to change render policy to RenderPolicy::Always, since otherwise render capture node won't work
103 engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::Always );
104
105 auto requestImageFcn = [&engine, scene]
106 {
107 if ( scene->sceneState() == Qgs3DMapScene::Ready )
108 {
109 engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::OnDemand );
111 }
112 };
113
114 auto saveImageFcn = [&evLoop, &resImage]( const QImage & img )
115 {
116 resImage = img;
117 evLoop.quit();
118 };
119
120 QMetaObject::Connection conn1 = QObject::connect( &engine, &QgsAbstract3DEngine::depthBufferCaptured, saveImageFcn );
121 QMetaObject::Connection conn2;
122
123 if ( scene->sceneState() == Qgs3DMapScene::Ready )
124 {
125 requestImageFcn();
126 }
127 else
128 {
129 // first wait until scene is loaded
130 conn2 = QObject::connect( scene, &Qgs3DMapScene::sceneStateChanged, requestImageFcn );
131 }
132
133 evLoop.exec();
134
135 QObject::disconnect( conn1 );
136 if ( conn2 )
137 QObject::disconnect( conn2 );
138
139 engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::OnDemand );
140 return resImage;
141}
142
144 Qgs3DMapSettings &mapSettings,
145 int framesPerSecond,
146 const QString &outputDirectory,
147 const QString &fileNameTemplate,
148 const QSize &outputSize,
149 QString &error,
150 QgsFeedback *feedback
151 )
152{
153 if ( animationSettings.keyFrames().size() < 2 )
154 {
155 error = QObject::tr( "Unable to export 3D animation. Add at least 2 keyframes" );
156 return false;
157 }
158
159 const float duration = animationSettings.duration(); //in seconds
160 if ( duration <= 0 )
161 {
162 error = QObject::tr( "Unable to export 3D animation (invalid duration)." );
163 return false;
164 }
165
166 float time = 0;
167 int frameNo = 0;
168 const int totalFrames = static_cast<int>( duration * framesPerSecond );
169
170 if ( fileNameTemplate.isEmpty() )
171 {
172 error = QObject::tr( "Filename template is empty" );
173 return false;
174 }
175
176 const int numberOfDigits = fileNameTemplate.count( QLatin1Char( '#' ) );
177 if ( numberOfDigits < 0 )
178 {
179 error = QObject::tr( "Wrong filename template format (must contain #)" );
180 return false;
181 }
182 const QString token( numberOfDigits, QLatin1Char( '#' ) );
183 if ( !fileNameTemplate.contains( token ) )
184 {
185 error = QObject::tr( "Filename template must contain all # placeholders in one continuous group." );
186 return false;
187 }
188
189 if ( !QDir().exists( outputDirectory ) )
190 {
191 if ( !QDir().mkpath( outputDirectory ) )
192 {
193 error = QObject::tr( "Output directory could not be created." );
194 return false;
195 }
196 }
197
199 engine.setSize( outputSize );
200 Qgs3DMapScene *scene = new Qgs3DMapScene( mapSettings, &engine );
201 engine.setRootEntity( scene );
202 // We need to change render policy to RenderPolicy::Always, since otherwise render capture node won't work
203 engine.renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::RenderPolicy::Always );
204
205 while ( time <= duration )
206 {
207
208 if ( feedback )
209 {
210 if ( feedback->isCanceled() )
211 {
212 error = QObject::tr( "Export canceled" );
213 return false;
214 }
215 feedback->setProgress( frameNo / static_cast<double>( totalFrames ) * 100 );
216 }
217 ++frameNo;
218
219 const Qgs3DAnimationSettings::Keyframe kf = animationSettings.interpolate( time );
220 scene->cameraController()->setLookingAtPoint( kf.point, kf.dist, kf.pitch, kf.yaw );
221
222 QString fileName( fileNameTemplate );
223 const QString frameNoPaddedLeft( QStringLiteral( "%1" ).arg( frameNo, numberOfDigits, 10, QChar( '0' ) ) ); // e.g. 0001
224 fileName.replace( token, frameNoPaddedLeft );
225 const QString path = QDir( outputDirectory ).filePath( fileName );
226
227 const QImage img = Qgs3DUtils::captureSceneImage( engine, scene );
228
229 img.save( path );
230
231 time += 1.0f / static_cast<float>( framesPerSecond );
232 }
233
234 return true;
235}
236
237
238int Qgs3DUtils::maxZoomLevel( double tile0width, double tileResolution, double maxError )
239{
240 if ( maxError <= 0 || tileResolution <= 0 || tile0width <= 0 )
241 return 0; // invalid input
242
243 // derived from:
244 // tile width [map units] = tile0width / 2^zoomlevel
245 // tile error [map units] = tile width / tile resolution
246 // + re-arranging to get zoom level if we know tile error we want to get
247 const double zoomLevel = -log( tileResolution * maxError / tile0width ) / log( 2 );
248 return round( zoomLevel ); // we could use ceil() here if we wanted to always get to the desired error
249}
250
252{
253 switch ( altClamp )
254 {
256 return QStringLiteral( "absolute" );
258 return QStringLiteral( "relative" );
260 return QStringLiteral( "terrain" );
261 }
263}
264
265
267{
268 if ( str == QLatin1String( "absolute" ) )
270 else if ( str == QLatin1String( "terrain" ) )
272 else // "relative" (default)
274}
275
276
278{
279 switch ( altBind )
280 {
282 return QStringLiteral( "vertex" );
284 return QStringLiteral( "centroid" );
285 }
287}
288
289
291{
292 if ( str == QLatin1String( "vertex" ) )
294 else // "centroid" (default)
296}
297
299{
300 switch ( mode )
301 {
303 return QStringLiteral( "no-culling" );
305 return QStringLiteral( "front" );
306 case Qgs3DTypes::Back:
307 return QStringLiteral( "back" );
309 return QStringLiteral( "front-and-back" );
310 }
312}
313
315{
316 if ( str == QLatin1String( "front" ) )
317 return Qgs3DTypes::Front;
318 else if ( str == QLatin1String( "back" ) )
319 return Qgs3DTypes::Back;
320 else if ( str == QLatin1String( "front-and-back" ) )
322 else
324}
325
326float Qgs3DUtils::clampAltitude( const QgsPoint &p, Qgis::AltitudeClamping altClamp, Qgis::AltitudeBinding altBind, float height, const QgsPoint &centroid, const Qgs3DMapSettings &map )
327{
328 float terrainZ = 0;
329 switch ( altClamp )
330 {
333 {
334 const QgsPointXY pt = altBind == Qgis::AltitudeBinding::Vertex ? p : centroid;
335 terrainZ = map.terrainRenderingEnabled() && map.terrainGenerator() ? map.terrainGenerator()->heightAt( pt.x(), pt.y(), map ) : 0;
336 break;
337 }
338
340 break;
341 }
342
343 float geomZ = 0;
344 if ( p.is3D() )
345 {
346 switch ( altClamp )
347 {
350 geomZ = p.z();
351 break;
352
354 break;
355 }
356 }
357
358 const float z = ( terrainZ + geomZ ) * map.terrainVerticalScale() + height;
359 return z;
360}
361
362void Qgs3DUtils::clampAltitudes( QgsLineString *lineString, Qgis::AltitudeClamping altClamp, Qgis::AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map )
363{
364 for ( int i = 0; i < lineString->nCoordinates(); ++i )
365 {
366 float terrainZ = 0;
367 switch ( altClamp )
368 {
371 {
372 QgsPointXY pt;
373 switch ( altBind )
374 {
376 pt.setX( lineString->xAt( i ) );
377 pt.setY( lineString->yAt( i ) );
378 break;
379
381 pt.set( centroid.x(), centroid.y() );
382 break;
383 }
384
385 terrainZ = map.terrainRenderingEnabled() && map.terrainGenerator() ? map.terrainGenerator()->heightAt( pt.x(), pt.y(), map ) : 0;
386 break;
387 }
388
390 break;
391 }
392
393 float geomZ = 0;
394
395 switch ( altClamp )
396 {
399 geomZ = lineString->zAt( i );
400 break;
401
403 break;
404 }
405
406 const float z = ( terrainZ + geomZ ) * map.terrainVerticalScale() + height;
407 lineString->setZAt( i, z );
408 }
409}
410
411
412bool Qgs3DUtils::clampAltitudes( QgsPolygon *polygon, Qgis::AltitudeClamping altClamp, Qgis::AltitudeBinding altBind, float height, const Qgs3DMapSettings &map )
413{
414 if ( !polygon->is3D() )
415 polygon->addZValue( 0 );
416
418 switch ( altBind )
419 {
421 break;
422
424 centroid = polygon->centroid();
425 break;
426 }
427
428 QgsCurve *curve = const_cast<QgsCurve *>( polygon->exteriorRing() );
429 QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( curve );
430 if ( !lineString )
431 return false;
432
433 clampAltitudes( lineString, altClamp, altBind, centroid, height, map );
434
435 for ( int i = 0; i < polygon->numInteriorRings(); ++i )
436 {
437 QgsCurve *curve = const_cast<QgsCurve *>( polygon->interiorRing( i ) );
438 QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( curve );
439 if ( !lineString )
440 return false;
441
442 clampAltitudes( lineString, altClamp, altBind, centroid, height, map );
443 }
444 return true;
445}
446
447
448QString Qgs3DUtils::matrix4x4toString( const QMatrix4x4 &m )
449{
450 const float *d = m.constData();
451 QStringList elems;
452 elems.reserve( 16 );
453 for ( int i = 0; i < 16; ++i )
454 elems << QString::number( d[i] );
455 return elems.join( ' ' );
456}
457
458QMatrix4x4 Qgs3DUtils::stringToMatrix4x4( const QString &str )
459{
460 QMatrix4x4 m;
461 float *d = m.data();
462 QStringList elems = str.split( ' ' );
463 for ( int i = 0; i < 16; ++i )
464 d[i] = elems[i].toFloat();
465 return m;
466}
467
468void Qgs3DUtils::extractPointPositions( const QgsFeature &f, const Qgs3DMapSettings &map, Qgis::AltitudeClamping altClamp, QVector<QVector3D> &positions )
469{
470 const QgsAbstractGeometry *g = f.geometry().constGet();
471 for ( auto it = g->vertices_begin(); it != g->vertices_end(); ++it )
472 {
473 const QgsPoint pt = *it;
474 float geomZ = 0;
475 if ( pt.is3D() )
476 {
477 geomZ = pt.z();
478 }
479 const float terrainZ = map.terrainRenderingEnabled() && map.terrainGenerator() ? map.terrainGenerator()->heightAt( pt.x(), pt.y(), map ) * map.terrainVerticalScale() : 0;
480 float h = 0.0f;
481 switch ( altClamp )
482 {
484 h = geomZ;
485 break;
487 h = terrainZ;
488 break;
490 h = terrainZ + geomZ;
491 break;
492 }
493 positions.append( QVector3D( pt.x() - map.origin().x(), h, -( pt.y() - map.origin().y() ) ) );
494 QgsDebugMsgLevel( QStringLiteral( "%1 %2 %3" ).arg( positions.last().x() ).arg( positions.last().y() ).arg( positions.last().z() ), 2 );
495 }
496}
497
503static inline uint outcode( QVector4D v )
504{
505 // For a discussion of outcodes see pg 388 Dunn & Parberry.
506 // For why you can't just test if the point is in a bounding box
507 // consider the case where a view frustum with view-size 1.5 x 1.5
508 // is tested against a 2x2 box which encloses the near-plane, while
509 // all the points in the box are outside the frustum.
510 // TODO: optimise this with assembler - according to D&P this can
511 // be done in one line of assembler on some platforms
512 uint code = 0;
513 if ( v.x() < -v.w() ) code |= 0x01;
514 if ( v.x() > v.w() ) code |= 0x02;
515 if ( v.y() < -v.w() ) code |= 0x04;
516 if ( v.y() > v.w() ) code |= 0x08;
517 if ( v.z() < -v.w() ) code |= 0x10;
518 if ( v.z() > v.w() ) code |= 0x20;
519 return code;
520}
521
522
533bool Qgs3DUtils::isCullable( const QgsAABB &bbox, const QMatrix4x4 &viewProjectionMatrix )
534{
535 uint out = 0xff;
536
537 for ( int i = 0; i < 8; ++i )
538 {
539 const QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
540 ( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
541 ( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );
542 const QVector4D pc = viewProjectionMatrix * p;
543
544 // if the logical AND of all the outcodes is non-zero then the BB is
545 // definitely outside the view frustum.
546 out = out & outcode( pc );
547 }
548 return out;
549}
550
552{
553 return QgsVector3D( mapCoords.x() - origin.x(),
554 mapCoords.z() - origin.z(),
555 -( mapCoords.y() - origin.y() ) );
556
557}
558
560{
561 return QgsVector3D( worldCoords.x() + origin.x(),
562 -worldCoords.z() + origin.y(),
563 worldCoords.y() + origin.z() );
564}
565
566static QgsRectangle _tryReprojectExtent2D( const QgsRectangle &extent, const QgsCoordinateReferenceSystem &crs1, const QgsCoordinateReferenceSystem &crs2, const QgsCoordinateTransformContext &context )
567{
568 QgsRectangle extentMapCrs( extent );
569 if ( crs1 != crs2 )
570 {
571 // reproject if necessary
572 QgsCoordinateTransform ct( crs1, crs2, context );
573 ct.setBallparkTransformsAreAppropriate( true );
574 try
575 {
576 extentMapCrs = ct.transformBoundingBox( extentMapCrs );
577 }
578 catch ( const QgsCsException & )
579 {
580 // bad luck, can't reproject for some reason
581 QgsDebugMsg( QStringLiteral( "3D utils: transformation of extent failed: " ) + extentMapCrs.toString( -1 ) );
582 }
583 }
584 return extentMapCrs;
585}
586
587QgsAABB Qgs3DUtils::layerToWorldExtent( const QgsRectangle &extent, double zMin, double zMax, const QgsCoordinateReferenceSystem &layerCrs, const QgsVector3D &mapOrigin, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &context )
588{
589 const QgsRectangle extentMapCrs( _tryReprojectExtent2D( extent, layerCrs, mapCrs, context ) );
590 return mapToWorldExtent( extentMapCrs, zMin, zMax, mapOrigin );
591}
592
594{
595 const QgsRectangle extentMap = worldToMapExtent( bbox, mapOrigin );
596 return _tryReprojectExtent2D( extentMap, mapCrs, layerCrs, context );
597}
598
599QgsAABB Qgs3DUtils::mapToWorldExtent( const QgsRectangle &extent, double zMin, double zMax, const QgsVector3D &mapOrigin )
600{
601 const QgsVector3D extentMin3D( extent.xMinimum(), extent.yMinimum(), zMin );
602 const QgsVector3D extentMax3D( extent.xMaximum(), extent.yMaximum(), zMax );
603 const QgsVector3D worldExtentMin3D = mapToWorldCoordinates( extentMin3D, mapOrigin );
604 const QgsVector3D worldExtentMax3D = mapToWorldCoordinates( extentMax3D, mapOrigin );
605 QgsAABB rootBbox( worldExtentMin3D.x(), worldExtentMin3D.y(), worldExtentMin3D.z(),
606 worldExtentMax3D.x(), worldExtentMax3D.y(), worldExtentMax3D.z() );
607 return rootBbox;
608}
609
611{
612 const QgsVector3D worldExtentMin3D = Qgs3DUtils::worldToMapCoordinates( QgsVector3D( bbox.xMin, bbox.yMin, bbox.zMin ), mapOrigin );
613 const QgsVector3D worldExtentMax3D = Qgs3DUtils::worldToMapCoordinates( QgsVector3D( bbox.xMax, bbox.yMax, bbox.zMax ), mapOrigin );
614 const QgsRectangle extentMap( worldExtentMin3D.x(), worldExtentMin3D.y(), worldExtentMax3D.x(), worldExtentMax3D.y() );
615 // we discard zMin/zMax here because we don't need it
616 return extentMap;
617}
618
619
621{
622 const QgsVector3D mapPoint1 = worldToMapCoordinates( worldPoint1, origin1 );
623 QgsVector3D mapPoint2 = mapPoint1;
624 if ( crs1 != crs2 )
625 {
626 // reproject if necessary
627 const QgsCoordinateTransform ct( crs1, crs2, context );
628 try
629 {
630 const QgsPointXY pt = ct.transform( QgsPointXY( mapPoint1.x(), mapPoint1.y() ) );
631 mapPoint2.set( pt.x(), pt.y(), mapPoint1.z() );
632 }
633 catch ( const QgsCsException & )
634 {
635 // bad luck, can't reproject for some reason
636 }
637 }
638 return mapToWorldCoordinates( mapPoint2, origin2 );
639}
640
641void Qgs3DUtils::estimateVectorLayerZRange( QgsVectorLayer *layer, double &zMin, double &zMax )
642{
643 if ( !QgsWkbTypes::hasZ( layer->wkbType() ) )
644 {
645 zMin = 0;
646 zMax = 0;
647 return;
648 }
649
650 zMin = std::numeric_limits<double>::max();
651 zMax = std::numeric_limits<double>::lowest();
652
653 QgsFeature f;
654 QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setNoAttributes().setLimit( 100 ) );
655 while ( it.nextFeature( f ) )
656 {
657 const QgsGeometry g = f.geometry();
658 for ( auto vit = g.vertices_begin(); vit != g.vertices_end(); ++vit )
659 {
660 const double z = ( *vit ).z();
661 if ( z < zMin ) zMin = z;
662 if ( z > zMax ) zMax = z;
663 }
664 }
665
666 if ( zMin == std::numeric_limits<double>::max() && zMax == std::numeric_limits<double>::lowest() )
667 {
668 zMin = 0;
669 zMax = 0;
670 }
671}
672
674{
675 QgsExpressionContext exprContext;
679 return exprContext;
680}
681
683{
685 settings.setAmbient( material->ambient() );
686 settings.setDiffuse( material->diffuse() );
687 settings.setSpecular( material->specular() );
688 settings.setShininess( material->shininess() );
689 return settings;
690}
691
692QgsRay3D Qgs3DUtils::rayFromScreenPoint( const QPoint &point, const QSize &windowSize, Qt3DRender::QCamera *camera )
693{
694 const QVector3D deviceCoords( point.x(), point.y(), 0.0 );
695 // normalized device coordinates
696 const QVector3D normDeviceCoords( 2.0 * deviceCoords.x() / windowSize.width() - 1.0f, 1.0f - 2.0 * deviceCoords.y() / windowSize.height(), camera->nearPlane() );
697 // clip coordinates
698 const QVector4D rayClip( normDeviceCoords.x(), normDeviceCoords.y(), -1.0, 0.0 );
699
700 const QMatrix4x4 invertedProjMatrix = camera->projectionMatrix().inverted();
701 const QMatrix4x4 invertedViewMatrix = camera->viewMatrix().inverted();
702
703 // ray direction in view coordinates
704 QVector4D rayDirView = invertedProjMatrix * rayClip;
705 // ray origin in world coordinates
706 const QVector4D rayOriginWorld = invertedViewMatrix * QVector4D( 0.0f, 0.0f, 0.0f, 1.0f );
707
708 // ray direction in world coordinates
709 rayDirView.setZ( -1.0f );
710 rayDirView.setW( 0.0f );
711 const QVector4D rayDirWorld4D = invertedViewMatrix * rayDirView;
712 QVector3D rayDirWorld( rayDirWorld4D.x(), rayDirWorld4D.y(), rayDirWorld4D.z() );
713 rayDirWorld = rayDirWorld.normalized();
714
715 return QgsRay3D( QVector3D( rayOriginWorld ), rayDirWorld );
716}
717
718QVector3D Qgs3DUtils::screenPointToWorldPos( const QPoint &screenPoint, double depth, const QSize &screenSize, Qt3DRender::QCamera *camera )
719{
720 double dNear = camera->nearPlane();
721 double dFar = camera->farPlane();
722 double distance = ( 2.0 * dNear * dFar ) / ( dFar + dNear - ( depth * 2 - 1 ) * ( dFar - dNear ) );
723
724 QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( screenPoint, screenSize, camera );
725 double dot = QVector3D::dotProduct( ray.direction(), camera->viewVector().normalized() );
726 distance /= dot;
727
728 return ray.origin() + distance * ray.direction();
729}
730
731void Qgs3DUtils::pitchAndYawFromViewVector( QVector3D vect, double &pitch, double &yaw )
732{
733 vect.normalize();
734
735 pitch = qRadiansToDegrees( qAcos( vect.y() ) );
736 yaw = qRadiansToDegrees( qAtan2( -vect.z(), vect.x() ) ) + 90;
737}
738
739QVector2D Qgs3DUtils::screenToTextureCoordinates( QVector2D screenXY, QSize winSize )
740{
741 return QVector2D( screenXY.x() / winSize.width(), 1 - screenXY.y() / winSize.width() );
742}
743
744QVector2D Qgs3DUtils::textureToScreenCoordinates( QVector2D textureXY, QSize winSize )
745{
746 return QVector2D( textureXY.x() * winSize.width(), ( 1 - textureXY.y() ) * winSize.height() );
747}
748
749std::unique_ptr<QgsPointCloudLayer3DRenderer> Qgs3DUtils::convert2DPointCloudRendererTo3D( QgsPointCloudRenderer *renderer )
750{
751 if ( !renderer )
752 return nullptr;
753
754 std::unique_ptr< QgsPointCloud3DSymbol > symbol3D;
755 if ( renderer->type() == QLatin1String( "ramp" ) )
756 {
757 const QgsPointCloudAttributeByRampRenderer *renderer2D = dynamic_cast< const QgsPointCloudAttributeByRampRenderer * >( renderer );
758 symbol3D = std::make_unique< QgsColorRampPointCloud3DSymbol >();
759 QgsColorRampPointCloud3DSymbol *symbol = static_cast< QgsColorRampPointCloud3DSymbol * >( symbol3D.get() );
760 symbol->setAttribute( renderer2D->attribute() );
761 symbol->setColorRampShaderMinMax( renderer2D->minimum(), renderer2D->maximum() );
762 symbol->setColorRampShader( renderer2D->colorRampShader() );
763 }
764 else if ( renderer->type() == QLatin1String( "rgb" ) )
765 {
766 const QgsPointCloudRgbRenderer *renderer2D = dynamic_cast< const QgsPointCloudRgbRenderer * >( renderer );
767 symbol3D = std::make_unique< QgsRgbPointCloud3DSymbol >();
768 QgsRgbPointCloud3DSymbol *symbol = static_cast< QgsRgbPointCloud3DSymbol * >( symbol3D.get() );
769 symbol->setRedAttribute( renderer2D->redAttribute() );
770 symbol->setGreenAttribute( renderer2D->greenAttribute() );
771 symbol->setBlueAttribute( renderer2D->blueAttribute() );
772
773 symbol->setRedContrastEnhancement( renderer2D->redContrastEnhancement() ? new QgsContrastEnhancement( *renderer2D->redContrastEnhancement() ) : nullptr );
774 symbol->setGreenContrastEnhancement( renderer2D->greenContrastEnhancement() ? new QgsContrastEnhancement( *renderer2D->greenContrastEnhancement() ) : nullptr );
775 symbol->setBlueContrastEnhancement( renderer2D->blueContrastEnhancement() ? new QgsContrastEnhancement( *renderer2D->blueContrastEnhancement() ) : nullptr );
776 }
777 else if ( renderer->type() == QLatin1String( "classified" ) )
778 {
779
780 const QgsPointCloudClassifiedRenderer *renderer2D = dynamic_cast< const QgsPointCloudClassifiedRenderer * >( renderer );
781 symbol3D = std::make_unique< QgsClassificationPointCloud3DSymbol >();
782 QgsClassificationPointCloud3DSymbol *symbol = static_cast< QgsClassificationPointCloud3DSymbol * >( symbol3D.get() );
783 symbol->setAttribute( renderer2D->attribute() );
784 symbol->setCategoriesList( renderer2D->categories() );
785 }
786
787 if ( symbol3D )
788 {
789 std::unique_ptr< QgsPointCloudLayer3DRenderer > renderer3D = std::make_unique< QgsPointCloudLayer3DRenderer >();
790 renderer3D->setSymbol( symbol3D.release() );
791 return renderer3D;
792 }
793 return nullptr;
794}
AltitudeClamping
Altitude clamping.
Definition: qgis.h:2189
@ Relative
Elevation is relative to terrain height (final elevation = terrain elevation + feature elevation)
@ Terrain
Elevation is clamped to terrain (final elevation = terrain elevation)
@ Absolute
Elevation is taken directly from feature and is independent of terrain height (final elevation = feat...
AltitudeBinding
Altitude binding.
Definition: qgis.h:2202
@ Centroid
Clamp just centroid of feature.
@ Vertex
Clamp every vertex of feature.
Keyframe interpolate(float time) const
Interpolates camera position and rotation at the given point in time.
float duration() const
Returns duration of the whole animation in seconds.
Keyframes keyFrames() const
Returns keyframes of the animation.
@ Ready
The scene is fully loaded/updated.
void sceneStateChanged()
Emitted when the scene's state has changed.
SceneState sceneState() const
Returns the current state of the scene.
QgsCameraController * cameraController()
Returns camera controller.
Definition: qgs3dmapscene.h:79
double terrainVerticalScale() const
Returns vertical scale (exaggeration) of terrain.
QgsTerrainGenerator * terrainGenerator() const
Returns the terrain generator.
bool terrainRenderingEnabled() const
Returns whether the 2D terrain surface will be rendered.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
CullingMode
Triangle culling mode.
Definition: qgs3dtypes.h:36
@ FrontAndBack
Will not render anything.
Definition: qgs3dtypes.h:40
@ NoCulling
Will render both front and back faces of triangles.
Definition: qgs3dtypes.h:37
@ Front
Will render only back faces of triangles.
Definition: qgs3dtypes.h:38
@ Back
Will render only front faces of triangles (recommended when input data are consistent)
Definition: qgs3dtypes.h:39
static QgsVector3D transformWorldCoordinates(const QgsVector3D &worldPoint1, const QgsVector3D &origin1, const QgsCoordinateReferenceSystem &crs1, const QgsVector3D &origin2, const QgsCoordinateReferenceSystem &crs2, const QgsCoordinateTransformContext &context)
Transforms a world point from (origin1, crs1) to (origin2, crs2)
Definition: qgs3dutils.cpp:620
static Qgs3DTypes::CullingMode cullingModeFromString(const QString &str)
Converts a string to a value from CullingMode enum.
Definition: qgs3dutils.cpp:314
static Qgis::AltitudeClamping altClampingFromString(const QString &str)
Converts a string to a value from AltitudeClamping enum.
Definition: qgs3dutils.cpp:266
static QString matrix4x4toString(const QMatrix4x4 &m)
Converts a 4x4 transform matrix to a string.
Definition: qgs3dutils.cpp:448
static QgsRectangle worldToMapExtent(const QgsAABB &bbox, const QgsVector3D &mapOrigin)
Converts axis aligned bounding box in 3D world coordinates to extent in map coordinates.
Definition: qgs3dutils.cpp:610
static QgsRectangle worldToLayerExtent(const QgsAABB &bbox, const QgsCoordinateReferenceSystem &layerCrs, const QgsVector3D &mapOrigin, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &context)
Converts axis aligned bounding box in 3D world coordinates to extent in map layer CRS.
Definition: qgs3dutils.cpp:593
static void pitchAndYawFromViewVector(QVector3D vect, double &pitch, double &yaw)
Function used to extract the pitch and yaw (also known as heading) angles in degrees from the view ve...
Definition: qgs3dutils.cpp:731
static int maxZoomLevel(double tile0width, double tileResolution, double maxError)
Calculates the highest needed zoom level for tiles in quad-tree given width of the base tile (zoom le...
Definition: qgs3dutils.cpp:238
static QgsAABB mapToWorldExtent(const QgsRectangle &extent, double zMin, double zMax, const QgsVector3D &mapOrigin)
Converts map extent to axis aligned bounding box in 3D world coordinates.
Definition: qgs3dutils.cpp:599
static QgsAABB layerToWorldExtent(const QgsRectangle &extent, double zMin, double zMax, const QgsCoordinateReferenceSystem &layerCrs, const QgsVector3D &mapOrigin, const QgsCoordinateReferenceSystem &mapCrs, const QgsCoordinateTransformContext &context)
Converts extent (in map layer's CRS) to axis aligned bounding box in 3D world coordinates.
Definition: qgs3dutils.cpp:587
static Qgis::AltitudeBinding altBindingFromString(const QString &str)
Converts a string to a value from AltitudeBinding enum.
Definition: qgs3dutils.cpp:290
static QString cullingModeToString(Qgs3DTypes::CullingMode mode)
Converts a value from CullingMode enum to a string.
Definition: qgs3dutils.cpp:298
static void extractPointPositions(const QgsFeature &f, const Qgs3DMapSettings &map, Qgis::AltitudeClamping altClamp, QVector< QVector3D > &positions)
Calculates (x,y,z) positions of (multi)point from the given feature.
Definition: qgs3dutils.cpp:468
static bool isCullable(const QgsAABB &bbox, const QMatrix4x4 &viewProjectionMatrix)
Returns true if bbox is completely outside the current viewing volume.
Definition: qgs3dutils.cpp:533
static QVector2D screenToTextureCoordinates(QVector2D screenXY, QSize winSize)
Converts from screen coordinates to texture coordinates.
Definition: qgs3dutils.cpp:739
static void estimateVectorLayerZRange(QgsVectorLayer *layer, double &zMin, double &zMax)
Try to estimate range of Z values used in the given vector layer and store that in zMin and zMax.
Definition: qgs3dutils.cpp:641
static QgsPhongMaterialSettings phongMaterialFromQt3DComponent(Qt3DExtras::QPhongMaterial *material)
Returns phong material settings object based on the Qt3D material.
Definition: qgs3dutils.cpp:682
static QString altClampingToString(Qgis::AltitudeClamping altClamp)
Converts a value from AltitudeClamping enum to a string.
Definition: qgs3dutils.cpp:251
static void clampAltitudes(QgsLineString *lineString, Qgis::AltitudeClamping altClamp, Qgis::AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map)
Clamps altitude of vertices of a linestring according to the settings.
Definition: qgs3dutils.cpp:362
static QMatrix4x4 stringToMatrix4x4(const QString &str)
Convert a string to a 4x4 transform matrix.
Definition: qgs3dutils.cpp:458
static QgsVector3D worldToMapCoordinates(const QgsVector3D &worldCoords, const QgsVector3D &origin)
Converts 3D world coordinates to map coordinates (applies offset and turns (x,y,z) into (x,...
Definition: qgs3dutils.cpp:559
static QgsVector3D mapToWorldCoordinates(const QgsVector3D &mapCoords, const QgsVector3D &origin)
Converts map coordinates to 3D world coordinates (applies offset and turns (x,y,z) into (x,...
Definition: qgs3dutils.cpp:551
static QVector2D textureToScreenCoordinates(QVector2D textureXY, QSize winSize)
Converts from texture coordinates coordinates to screen coordinates.
Definition: qgs3dutils.cpp:744
static bool exportAnimation(const Qgs3DAnimationSettings &animationSettings, Qgs3DMapSettings &mapSettings, int framesPerSecond, const QString &outputDirectory, const QString &fileNameTemplate, const QSize &outputSize, QString &error, QgsFeedback *feedback=nullptr)
Captures 3D animation frames to the selected folder.
Definition: qgs3dutils.cpp:143
static QVector3D screenPointToWorldPos(const QPoint &screenPoint, double depth, const QSize &screenSize, Qt3DRender::QCamera *camera)
Converts the clicked mouse position to the corresponding 3D world coordinates.
Definition: qgs3dutils.cpp:718
static QString altBindingToString(Qgis::AltitudeBinding altBind)
Converts a value from AltitudeBinding enum to a string.
Definition: qgs3dutils.cpp:277
static float clampAltitude(const QgsPoint &p, Qgis::AltitudeClamping altClamp, Qgis::AltitudeBinding altBind, float height, const QgsPoint &centroid, const Qgs3DMapSettings &map)
Clamps altitude of a vertex according to the settings, returns Z value.
Definition: qgs3dutils.cpp:326
static QgsRay3D rayFromScreenPoint(const QPoint &point, const QSize &windowSize, Qt3DRender::QCamera *camera)
Convert from clicked point on the screen to a ray in world coordinates.
Definition: qgs3dutils.cpp:692
static QImage captureSceneImage(QgsAbstract3DEngine &engine, Qgs3DMapScene *scene)
Captures image of the current 3D scene of a 3D engine.
Definition: qgs3dutils.cpp:51
static std::unique_ptr< QgsPointCloudLayer3DRenderer > convert2DPointCloudRendererTo3D(QgsPointCloudRenderer *renderer)
Creates a QgsPointCloudLayer3DRenderer matching the symbol settings of a given QgsPointCloudRenderer.
Definition: qgs3dutils.cpp:749
static QImage captureSceneDepthBuffer(QgsAbstract3DEngine &engine, Qgs3DMapScene *scene)
Captures the depth buffer of the current 3D scene of a 3D engine.
Definition: qgs3dutils.cpp:97
static QgsExpressionContext globalProjectLayerExpressionContext(QgsVectorLayer *layer)
Returns expression context for use in preparation of 3D data of a layer.
Definition: qgs3dutils.cpp:673
3
Definition: qgsaabb.h:34
float yMax
Definition: qgsaabb.h:88
float xMax
Definition: qgsaabb.h:87
float xMin
Definition: qgsaabb.h:84
float zMax
Definition: qgsaabb.h:89
float yMin
Definition: qgsaabb.h:85
float zMin
Definition: qgsaabb.h:86
void requestCaptureImage()
Starts a request for an image rendered by the engine.
void requestDepthBufferCapture()
Starts a request for an image containing the depth buffer data of the engine.
void imageCaptured(const QImage &image)
Emitted after a call to requestCaptureImage() to return the captured image.
void depthBufferCaptured(const QImage &image)
Emitted after a call to requestDepthBufferCapture() to return the captured depth buffer.
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
Abstract base class for all geometries.
vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
virtual QgsPoint centroid() const
Returns the centroid of the geometry.
void setLookingAtPoint(const QgsVector3D &point, float distance, float pitch, float yaw)
Sets the complete camera configuration: the point towards it is looking (in 3D world coordinates),...
void setCategoriesList(const QgsPointCloudCategoryList &categories)
Sets the list of categories of the classification.
void setAttribute(const QString &attribute)
Sets the attribute used to select the color of the point cloud.
void setAttribute(const QString &attribute)
Sets the attribute used to select the color of the point cloud.
void setColorRampShaderMinMax(double min, double max)
Sets the minimum and maximum values used when classifying colors in the color ramp shader.
void setColorRampShader(const QgsColorRampShader &colorRampShader)
Sets the color ramp shader used to render the point cloud.
Manipulates raster or point cloud pixel values so that they enhanceContrast or clip into a specified ...
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
void setZAt(int index, double z)
Sets the z-coordinate of the specified node in the line string.
int nCoordinates() const override SIP_HOLDGIL
Returns the number of nodes contained in the geometry.
double zAt(int index) const
Returns the z-coordinate of the specified node in the line string.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
void setSize(QSize s) override
Sets the size of the rendering area (in pixels)
void setRootEntity(Qt3DCore::QEntity *root) override
Sets root entity of the 3D scene.
Qt3DRender::QRenderSettings * renderSettings() override
Returns access to the engine's render settings (the frame graph can be accessed from here)
void setDiffuse(const QColor &diffuse)
Sets diffuse color component.
void setAmbient(const QColor &ambient)
Sets ambient color component.
void setShininess(float shininess)
Sets shininess of the surface.
void setSpecular(const QColor &specular)
Sets specular color component.
An RGB renderer for 2d visualisation of point clouds using embedded red, green and blue attributes.
double maximum() const
Returns the maximum value for attributes which will be used by the color ramp shader.
QgsColorRampShader colorRampShader() const
Returns the color ramp shader function used to visualize the attribute.
double minimum() const
Returns the minimum value for attributes which will be used by the color ramp shader.
QString attribute() const
Returns the attribute to use for the renderer.
Renders point clouds by a classification attribute.
QString attribute() const
Returns the attribute to use for the renderer.
QgsPointCloudCategoryList categories() const
Returns the classification categories used for rendering.
Abstract base class for 2d point cloud renderers.
virtual QString type() const =0
Returns the identifier of the renderer type.
An RGB renderer for 2d visualisation of point clouds using embedded red, green and blue attributes.
QString redAttribute() const
Returns the attribute to use for the red channel.
QString greenAttribute() const
Returns the attribute to use for the green channel.
const QgsContrastEnhancement * greenContrastEnhancement() const
Returns the contrast enhancement to use for the green channel.
QString blueAttribute() const
Returns the attribute to use for the blue channel.
const QgsContrastEnhancement * blueContrastEnhancement() const
Returns the contrast enhancement to use for the blue channel.
const QgsContrastEnhancement * redContrastEnhancement() const
Returns the contrast enhancement to use for the red channel.
A class to represent a 2D point.
Definition: qgspointxy.h:59
void set(double x, double y) SIP_HOLDGIL
Sets the x and y value of the point.
Definition: qgspointxy.h:139
void setX(double x) SIP_HOLDGIL
Sets the x value of the point.
Definition: qgspointxy.h:122
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
void setY(double y) SIP_HOLDGIL
Sets the y value of the point.
Definition: qgspointxy.h:132
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:34
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
A representation of a ray in 3D.
Definition: qgsray3d.h:31
QVector3D origin() const
Returns the origin of the ray.
Definition: qgsray3d.h:44
QVector3D direction() const
Returns the direction of the ray see setDirection()
Definition: qgsray3d.h:50
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
void setBlueAttribute(const QString &attribute)
Sets the attribute to use for the blue channel.
void setGreenContrastEnhancement(QgsContrastEnhancement *enhancement SIP_TRANSFER)
Sets the contrast enhancement to use for the green channel.
void setGreenAttribute(const QString &attribute)
Sets the attribute to use for the green channel.
void setBlueContrastEnhancement(QgsContrastEnhancement *enhancement SIP_TRANSFER)
Sets the contrast enhancement to use for the blue channel.
void setRedContrastEnhancement(QgsContrastEnhancement *enhancement SIP_TRANSFER)
Sets the contrast enhancement to use for the red channel.
void setRedAttribute(const QString &attribute)
Sets the attribute to use for the red channel.
virtual float heightAt(double x, double y, const Qgs3DMapSettings &map) const
Returns height at (x,y) in terrain's CRS.
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
void set(double x, double y, double z)
Sets vector coordinates.
Definition: qgsvector3d.h:56
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
CORE_EXPORT QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
#define str(x)
Definition: qgis.cpp:37
#define BUILTIN_UNREACHABLE
Definition: qgis.h:3748
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
float pitch
Tilt of the camera in degrees (0 = looking from the top, 90 = looking from the side,...
float yaw
Horizontal rotation around the focal point in degrees.
QgsVector3D point
Point towards which the camera is looking in 3D world coords.
float dist
Distance of the camera from the focal point.