QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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"
25 
26 #include "qgs3dmapscene.h"
27 #include "qgsabstract3dengine.h"
28 #include "qgsterraingenerator.h"
29 
30 
32 {
33  QImage resImage;
34  QEventLoop evLoop;
35 
36  auto requestImageFcn = [&engine, scene]
37  {
38  if ( scene->sceneState() == Qgs3DMapScene::Ready )
39  {
40  engine.requestCaptureImage();
41  }
42  };
43 
44  auto saveImageFcn = [&evLoop, &resImage]( const QImage & img )
45  {
46  resImage = img;
47  evLoop.quit();
48  };
49 
50  QMetaObject::Connection conn1 = QObject::connect( &engine, &QgsAbstract3DEngine::imageCaptured, saveImageFcn );
51  QMetaObject::Connection conn2;
52 
53  if ( scene->sceneState() == Qgs3DMapScene::Ready )
54  {
55  requestImageFcn();
56  }
57  else
58  {
59  // first wait until scene is loaded
60  conn2 = QObject::connect( scene, &Qgs3DMapScene::sceneStateChanged, requestImageFcn );
61  }
62 
63  evLoop.exec();
64 
65  QObject::disconnect( conn1 );
66  if ( conn2 )
67  QObject::disconnect( conn2 );
68 
69  return resImage;
70 }
71 
72 
73 int Qgs3DUtils::maxZoomLevel( double tile0width, double tileResolution, double maxError )
74 {
75  if ( maxError <= 0 || tileResolution <= 0 || tile0width <= 0 )
76  return 0; // invalid input
77 
78  // derived from:
79  // tile width [map units] = tile0width / 2^zoomlevel
80  // tile error [map units] = tile width / tile resolution
81  // + re-arranging to get zoom level if we know tile error we want to get
82  double zoomLevel = -log( tileResolution * maxError / tile0width ) / log( 2 );
83  return round( zoomLevel ); // we could use ceil() here if we wanted to always get to the desired error
84 }
85 
87 {
88  switch ( altClamp )
89  {
90  case Qgs3DTypes::AltClampAbsolute: return QStringLiteral( "absolute" );
91  case Qgs3DTypes::AltClampRelative: return QStringLiteral( "relative" );
92  case Qgs3DTypes::AltClampTerrain: return QStringLiteral( "terrain" );
93  default: Q_ASSERT( false ); return QString();
94  }
95 }
96 
97 
99 {
100  if ( str == QLatin1String( "absolute" ) )
102  else if ( str == QLatin1String( "terrain" ) )
104  else // "relative" (default)
106 }
107 
108 
110 {
111  switch ( altBind )
112  {
113  case Qgs3DTypes::AltBindVertex: return QStringLiteral( "vertex" );
114  case Qgs3DTypes::AltBindCentroid: return QStringLiteral( "centroid" );
115  default: Q_ASSERT( false ); return QString();
116  }
117 }
118 
119 
121 {
122  if ( str == QLatin1String( "vertex" ) )
124  else // "centroid" (default)
126 }
127 
129 {
130  switch ( mode )
131  {
132  case Qgs3DTypes::NoCulling: return QStringLiteral( "no-culling" );
133  case Qgs3DTypes::Front: return QStringLiteral( "front" );
134  case Qgs3DTypes::Back: return QStringLiteral( "back" );
135  case Qgs3DTypes::FrontAndBack: return QStringLiteral( "front-and-back" );
136  }
137  return QString();
138 }
139 
141 {
142  if ( str == QStringLiteral( "front" ) )
143  return Qgs3DTypes::Front;
144  else if ( str == QStringLiteral( "back" ) )
145  return Qgs3DTypes::Back;
146  else if ( str == QStringLiteral( "front-and-back" ) )
148  else
149  return Qgs3DTypes::NoCulling;
150 }
151 
152 float Qgs3DUtils::clampAltitude( const QgsPoint &p, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, float height, const QgsPoint &centroid, const Qgs3DMapSettings &map )
153 {
154  float terrainZ = 0;
155  if ( altClamp == Qgs3DTypes::AltClampRelative || altClamp == Qgs3DTypes::AltClampTerrain )
156  {
157  QgsPointXY pt = altBind == Qgs3DTypes::AltBindVertex ? p : centroid;
158  terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map );
159  }
160 
161  float geomZ = altClamp == Qgs3DTypes::AltClampAbsolute || altClamp == Qgs3DTypes::AltClampRelative ? p.z() : 0;
162 
163  float z = ( terrainZ + geomZ ) * map.terrainVerticalScale() + height;
164  return z;
165 }
166 
167 void Qgs3DUtils::clampAltitudes( QgsLineString *lineString, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map )
168 {
169  for ( int i = 0; i < lineString->nCoordinates(); ++i )
170  {
171  float terrainZ = 0;
172  if ( altClamp == Qgs3DTypes::AltClampRelative || altClamp == Qgs3DTypes::AltClampTerrain )
173  {
174  QgsPointXY pt;
175  if ( altBind == Qgs3DTypes::AltBindVertex )
176  {
177  pt.setX( lineString->xAt( i ) );
178  pt.setY( lineString->yAt( i ) );
179  }
180  else
181  {
182  pt.set( centroid.x(), centroid.y() );
183  }
184  terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map );
185  }
186 
187  float geomZ = 0;
188  if ( altClamp == Qgs3DTypes::AltClampAbsolute || altClamp == Qgs3DTypes::AltClampRelative )
189  geomZ = lineString->zAt( i );
190 
191  float z = ( terrainZ + geomZ ) * map.terrainVerticalScale() + height;
192  lineString->setZAt( i, z );
193  }
194 }
195 
196 
198 {
199  if ( !polygon->is3D() )
200  polygon->addZValue( 0 );
201 
202  QgsPoint centroid;
203  if ( altBind == Qgs3DTypes::AltBindCentroid )
204  centroid = polygon->centroid();
205 
206  QgsCurve *curve = const_cast<QgsCurve *>( polygon->exteriorRing() );
207  QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( curve );
208  if ( !lineString )
209  return false;
210 
211  clampAltitudes( lineString, altClamp, altBind, centroid, height, map );
212 
213  for ( int i = 0; i < polygon->numInteriorRings(); ++i )
214  {
215  QgsCurve *curve = const_cast<QgsCurve *>( polygon->interiorRing( i ) );
216  QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( curve );
217  if ( !lineString )
218  return false;
219 
220  clampAltitudes( lineString, altClamp, altBind, centroid, height, map );
221  }
222  return true;
223 }
224 
225 
226 QString Qgs3DUtils::matrix4x4toString( const QMatrix4x4 &m )
227 {
228  const float *d = m.constData();
229  QStringList elems;
230  elems.reserve( 16 );
231  for ( int i = 0; i < 16; ++i )
232  elems << QString::number( d[i] );
233  return elems.join( ' ' );
234 }
235 
236 QMatrix4x4 Qgs3DUtils::stringToMatrix4x4( const QString &str )
237 {
238  QMatrix4x4 m;
239  float *d = m.data();
240  QStringList elems = str.split( ' ' );
241  for ( int i = 0; i < 16; ++i )
242  d[i] = elems[i].toFloat();
243  return m;
244 }
245 
246 QList<QVector3D> Qgs3DUtils::positions( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsFeatureRequest &request, Qgs3DTypes::AltitudeClamping altClamp )
247 {
248  QList<QVector3D> positions;
249  QgsFeature f;
250  QgsFeatureIterator fi = layer->getFeatures( request );
251  while ( fi.nextFeature( f ) )
252  {
253  if ( f.geometry().isNull() )
254  continue;
255 
256  const QgsAbstractGeometry *g = f.geometry().constGet();
257  for ( auto it = g->vertices_begin(); it != g->vertices_end(); ++it )
258  {
259  QgsPoint pt = *it;
260  float geomZ = 0;
261  if ( pt.is3D() )
262  {
263  geomZ = pt.z();
264  }
265  float terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map ) * map.terrainVerticalScale();
266  float h;
267  switch ( altClamp )
268  {
270  default:
271  h = geomZ;
272  break;
274  h = terrainZ;
275  break;
277  h = terrainZ + geomZ;
278  break;
279  }
280  positions.append( QVector3D( pt.x() - map.origin().x(), h, -( pt.y() - map.origin().y() ) ) );
281  //qDebug() << positions.last();
282  }
283  }
284 
285  return positions;
286 }
287 
293 static inline uint outcode( QVector4D v )
294 {
295  // For a discussion of outcodes see pg 388 Dunn & Parberry.
296  // For why you can't just test if the point is in a bounding box
297  // consider the case where a view frustum with view-size 1.5 x 1.5
298  // is tested against a 2x2 box which encloses the near-plane, while
299  // all the points in the box are outside the frustum.
300  // TODO: optimise this with assembler - according to D&P this can
301  // be done in one line of assembler on some platforms
302  uint code = 0;
303  if ( v.x() < -v.w() ) code |= 0x01;
304  if ( v.x() > v.w() ) code |= 0x02;
305  if ( v.y() < -v.w() ) code |= 0x04;
306  if ( v.y() > v.w() ) code |= 0x08;
307  if ( v.z() < -v.w() ) code |= 0x10;
308  if ( v.z() > v.w() ) code |= 0x20;
309  return code;
310 }
311 
312 
323 bool Qgs3DUtils::isCullable( const QgsAABB &bbox, const QMatrix4x4 &viewProjectionMatrix )
324 {
325  uint out = 0xff;
326 
327  for ( int i = 0; i < 8; ++i )
328  {
329  QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
330  ( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
331  ( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );
332  QVector4D pc = viewProjectionMatrix * p;
333 
334  // if the logical AND of all the outcodes is non-zero then the BB is
335  // definitely outside the view frustum.
336  out = out & outcode( pc );
337  }
338  return out;
339 }
340 
342 {
343  return QgsVector3D( mapCoords.x() - origin.x(),
344  mapCoords.z() - origin.z(),
345  -( mapCoords.y() - origin.y() ) );
346 
347 }
348 
350 {
351  return QgsVector3D( worldCoords.x() + origin.x(),
352  -worldCoords.z() + origin.y(),
353  worldCoords.y() + origin.z() );
354 }
355 
357 {
358  QgsVector3D mapPoint1 = worldToMapCoordinates( worldPoint1, origin1 );
359  QgsVector3D mapPoint2 = mapPoint1;
360  if ( crs1 != crs2 )
361  {
362  // reproject if necessary
363  QgsCoordinateTransform ct( crs1, crs2, context );
364  try
365  {
366  QgsPointXY pt = ct.transform( QgsPointXY( mapPoint1.x(), mapPoint1.y() ) );
367  mapPoint2.set( pt.x(), pt.y(), mapPoint1.z() );
368  }
369  catch ( const QgsCsException & )
370  {
371  // bad luck, can't reproject for some reason
372  }
373  }
374  return mapToWorldCoordinates( mapPoint2, origin2 );
375 }
376 
AltitudeClamping
how to handle altitude of vector features
Definition: qgs3dtypes.h:34
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
Wrapper for iterator of features from vector data provider or vector layer.
3 Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double preci...
Definition: qgsvector3d.h:31
3 Axis-aligned bounding box - in world coords.
Definition: qgsaabb.h:30
void set(double x, double y)
Sets the x and y value of the point.
Definition: qgspointxy.h:119
double y
Definition: qgspoint.h:42
static QString altClampingToString(Qgs3DTypes::AltitudeClamping altClamp)
Converts a value from AltitudeClamping enum to a string.
Definition: qgs3dutils.cpp:86
static Qgs3DTypes::CullingMode cullingModeFromString(const QString &str)
Converts a string to a value from CullingMode enum.
Definition: qgs3dutils.cpp:140
virtual float heightAt(double x, double y, const Qgs3DMapSettings &map) const
Returns height at (x,y) in terrain&#39;s CRS.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
float zMax
Definition: qgsaabb.h:80
CullingMode
Triangle culling mode.
Definition: qgs3dtypes.h:49
static Qgs3DTypes::AltitudeClamping altClampingFromString(const QString &str)
Converts a string to a value from AltitudeClamping enum.
Definition: qgs3dutils.cpp:98
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static bool isCullable(const QgsAABB &bbox, const QMatrix4x4 &viewProjectionMatrix)
Returns true if bbox is completely outside the current viewing volume.
Definition: qgs3dutils.cpp:323
Will not render anything.
Definition: qgs3dtypes.h:54
vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
3 Definition of the world
Z_final = z_geometry.
Definition: qgs3dtypes.h:36
void set(double x, double y, double z)
Sets vector coordinates.
Definition: qgsvector3d.h:56
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
Clamp every vertex of feature.
Definition: qgs3dtypes.h:44
float zMin
Definition: qgsaabb.h:77
Will render only front faces of triangles (recommended when input data are consistent) ...
Definition: qgs3dtypes.h:53
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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:349
vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry...
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
float yMax
Definition: qgsaabb.h:79
static QString cullingModeToString(Qgs3DTypes::CullingMode mode)
Converts a value from CullingMode enum to a string.
Definition: qgs3dutils.cpp:128
Will render only back faces of triangles.
Definition: qgs3dtypes.h:52
Will render both front and back faces of triangles.
Definition: qgs3dtypes.h:51
SceneState sceneState() const
Returns the current state of the scene.
Definition: qgs3dmapscene.h:79
virtual void requestCaptureImage()=0
Starts a request for an image rendered by the engine.
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
void setY(double y)
Sets the y value of the point.
Definition: qgspointxy.h:113
This class wraps a request for features to a vector layer (or directly its vector data provider)...
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
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:341
3 Base class for 3D engine implementation.
static float clampAltitude(const QgsPoint &p, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::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:152
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
The scene is fully loaded/updated.
Definition: qgs3dmapscene.h:74
Abstract base class for all geometries.
Contains information about the context in which a coordinate transform is executed.
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:73
void setZAt(int index, double z)
Sets the z-coordinate of the specified node in the line string.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
AltitudeBinding
how to handle clamping of vertices of individual features
Definition: qgs3dtypes.h:42
float xMin
Definition: qgsaabb.h:75
void setX(double x)
Sets the x value of the point.
Definition: qgspointxy.h:104
double x
Definition: qgspointxy.h:47
static QImage captureSceneImage(QgsAbstract3DEngine &engine, Qgs3DMapScene *scene)
Captures image of the current 3D scene of a 3D engine.
Definition: qgs3dutils.cpp:31
float xMax
Definition: qgsaabb.h:78
double zAt(int index) const
Returns the z-coordinate of the specified node in the line string.
virtual QgsPoint centroid() const
Returns the centroid of the geometry.
3 Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children...
Definition: qgs3dmapscene.h:53
void imageCaptured(const QImage &image)
Emitted after a call to requestCaptureImage() to return the captured image.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
Z_final = z_terrain.
Definition: qgs3dtypes.h:38
static QMatrix4x4 stringToMatrix4x4(const QString &str)
Convert a string to a 4x4 transform matrix.
Definition: qgs3dutils.cpp:236
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
float yMin
Definition: qgsaabb.h:76
This class represents a coordinate reference system (CRS).
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
void sceneStateChanged()
Emitted when the scene&#39;s state has changed.
static Qgs3DTypes::AltitudeBinding altBindingFromString(const QString &str)
Converts a string to a value from AltitudeBinding enum.
Definition: qgs3dutils.cpp:120
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Class for doing transforms between two map coordinate systems.
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
static void clampAltitudes(QgsLineString *lineString, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map)
Clamps altitude of vertices of a linestring according to the settings.
Definition: qgs3dutils.cpp:167
double z
Definition: qgspoint.h:43
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
QgsGeometry geometry
Definition: qgsfeature.h:67
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
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:356
bool nextFeature(QgsFeature &f)
static QList< QVector3D > positions(const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsFeatureRequest &req, Qgs3DTypes::AltitudeClamping altClamp)
Calculates (x,y,z) positions of a (multi)point in the Point vector layers.
Definition: qgs3dutils.cpp:246
Polygon geometry type.
Definition: qgspolygon.h:31
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
Represents a vector layer which manages a vector based data sets.
double terrainVerticalScale() const
Returns vertical scale (exaggeration) of terrain.
Z_final = z_terrain + z_geometry.
Definition: qgs3dtypes.h:37
static QString matrix4x4toString(const QMatrix4x4 &m)
Converts a 4x4 transform matrix to a string.
Definition: qgs3dutils.cpp:226
static QString altBindingToString(Qgs3DTypes::AltitudeBinding altBind)
Converts a value from AltitudeBinding enum to a string.
Definition: qgs3dutils.cpp:109
Clamp just centroid of feature.
Definition: qgs3dtypes.h:45
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data...
double x
Definition: qgspoint.h:41