QGIS API Documentation  3.2.0-Bonn (bc43194)
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 "qgsterraingenerator.h"
27 
28 
29 
30 int Qgs3DUtils::maxZoomLevel( double tile0width, double tileResolution, double maxError )
31 {
32  if ( maxError <= 0 || tileResolution <= 0 || tile0width <= 0 )
33  return 0; // invalid input
34 
35  // derived from:
36  // tile width [map units] = tile0width / 2^zoomlevel
37  // tile error [map units] = tile width / tile resolution
38  // + re-arranging to get zoom level if we know tile error we want to get
39  double zoomLevel = -log( tileResolution * maxError / tile0width ) / log( 2 );
40  return round( zoomLevel ); // we could use ceil() here if we wanted to always get to the desired error
41 }
42 
44 {
45  switch ( altClamp )
46  {
47  case AltClampAbsolute: return QStringLiteral( "absolute" );
48  case AltClampRelative: return QStringLiteral( "relative" );
49  case AltClampTerrain: return QStringLiteral( "terrain" );
50  default: Q_ASSERT( false ); return QString();
51  }
52 }
53 
54 
56 {
57  if ( str == "absolute" )
58  return AltClampAbsolute;
59  else if ( str == "terrain" )
60  return AltClampTerrain;
61  else // "relative" (default)
62  return AltClampRelative;
63 }
64 
65 
67 {
68  switch ( altBind )
69  {
70  case AltBindVertex: return QStringLiteral( "vertex" );
71  case AltBindCentroid: return QStringLiteral( "centroid" );
72  default: Q_ASSERT( false ); return QString();
73  }
74 }
75 
76 
78 {
79  if ( str == "vertex" )
80  return AltBindVertex;
81  else // "centroid" (default)
82  return AltBindCentroid;
83 }
84 
85 QString Qgs3DUtils::cullingModeToString( Qt3DRender::QCullFace::CullingMode mode )
86 {
87  switch ( mode )
88  {
89  case Qt3DRender::QCullFace::NoCulling: return QStringLiteral( "no-culling" );
90  case Qt3DRender::QCullFace::Front: return QStringLiteral( "front" );
91  case Qt3DRender::QCullFace::Back: return QStringLiteral( "back" );
92  case Qt3DRender::QCullFace::FrontAndBack: return QStringLiteral( "front-and-back" );
93  }
94  return QString();
95 }
96 
97 Qt3DRender::QCullFace::CullingMode Qgs3DUtils::cullingModeFromString( const QString &str )
98 {
99  if ( str == QStringLiteral( "front" ) )
100  return Qt3DRender::QCullFace::Front;
101  else if ( str == QStringLiteral( "back" ) )
102  return Qt3DRender::QCullFace::Back;
103  else if ( str == QStringLiteral( "front-and-back" ) )
104  return Qt3DRender::QCullFace::FrontAndBack;
105  else
106  return Qt3DRender::QCullFace::NoCulling;
107 }
108 
109 
110 void Qgs3DUtils::clampAltitudes( QgsLineString *lineString, AltitudeClamping altClamp, AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map )
111 {
112  for ( int i = 0; i < lineString->nCoordinates(); ++i )
113  {
114  float terrainZ = 0;
115  if ( altClamp == AltClampRelative || altClamp == AltClampTerrain )
116  {
117  QgsPointXY pt;
118  if ( altBind == AltBindVertex )
119  {
120  pt.setX( lineString->xAt( i ) );
121  pt.setY( lineString->yAt( i ) );
122  }
123  else
124  {
125  pt.set( centroid.x(), centroid.y() );
126  }
127  terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map );
128  }
129 
130  float geomZ = 0;
131  if ( altClamp == AltClampAbsolute || altClamp == AltClampRelative )
132  geomZ = lineString->zAt( i );
133 
134  float z = ( terrainZ + geomZ ) * map.terrainVerticalScale() + height;
135  lineString->setZAt( i, z );
136  }
137 }
138 
139 
140 bool Qgs3DUtils::clampAltitudes( QgsPolygon *polygon, AltitudeClamping altClamp, AltitudeBinding altBind, float height, const Qgs3DMapSettings &map )
141 {
142  if ( !polygon->is3D() )
143  polygon->addZValue( 0 );
144 
145  QgsPoint centroid;
146  if ( altBind == AltBindCentroid )
147  centroid = polygon->centroid();
148 
149  QgsCurve *curve = const_cast<QgsCurve *>( polygon->exteriorRing() );
150  QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( curve );
151  if ( !lineString )
152  return false;
153 
154  clampAltitudes( lineString, altClamp, altBind, centroid, height, map );
155 
156  for ( int i = 0; i < polygon->numInteriorRings(); ++i )
157  {
158  QgsCurve *curve = const_cast<QgsCurve *>( polygon->interiorRing( i ) );
159  QgsLineString *lineString = qgsgeometry_cast<QgsLineString *>( curve );
160  if ( !lineString )
161  return false;
162 
163  clampAltitudes( lineString, altClamp, altBind, centroid, height, map );
164  }
165  return true;
166 }
167 
168 
169 QString Qgs3DUtils::matrix4x4toString( const QMatrix4x4 &m )
170 {
171  const float *d = m.constData();
172  QStringList elems;
173  for ( int i = 0; i < 16; ++i )
174  elems << QString::number( d[i] );
175  return elems.join( ' ' );
176 }
177 
178 QMatrix4x4 Qgs3DUtils::stringToMatrix4x4( const QString &str )
179 {
180  QMatrix4x4 m;
181  float *d = m.data();
182  QStringList elems = str.split( ' ' );
183  for ( int i = 0; i < 16; ++i )
184  d[i] = elems[i].toFloat();
185  return m;
186 }
187 
188 QList<QVector3D> Qgs3DUtils::positions( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsFeatureRequest &request, AltitudeClamping altClamp )
189 {
190  QList<QVector3D> positions;
191  QgsFeature f;
192  QgsFeatureIterator fi = layer->getFeatures( request );
193  while ( fi.nextFeature( f ) )
194  {
195  if ( f.geometry().isNull() )
196  continue;
197 
198  const QgsAbstractGeometry *g = f.geometry().constGet();
199  for ( auto it = g->vertices_begin(); it != g->vertices_end(); ++it )
200  {
201  QgsPoint pt = *it;
202  float geomZ = 0;
203  if ( pt.is3D() )
204  {
205  geomZ = pt.z();
206  }
207  float terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map ) * map.terrainVerticalScale();
208  float h;
209  switch ( altClamp )
210  {
211  case AltClampAbsolute:
212  h = geomZ;
213  break;
214  case AltClampTerrain:
215  h = terrainZ;
216  break;
217  case AltClampRelative:
218  h = terrainZ + geomZ;
219  break;
220  }
221  positions.append( QVector3D( pt.x() - map.origin().x(), h, -( pt.y() - map.origin().y() ) ) );
222  //qDebug() << positions.last();
223  }
224  }
225 
226  return positions;
227 }
228 
234 static inline uint outcode( const QVector4D &v )
235 {
236  // For a discussion of outcodes see pg 388 Dunn & Parberry.
237  // For why you can't just test if the point is in a bounding box
238  // consider the case where a view frustum with view-size 1.5 x 1.5
239  // is tested against a 2x2 box which encloses the near-plane, while
240  // all the points in the box are outside the frustum.
241  // TODO: optimise this with assembler - according to D&P this can
242  // be done in one line of assembler on some platforms
243  uint code = 0;
244  if ( v.x() < -v.w() ) code |= 0x01;
245  if ( v.x() > v.w() ) code |= 0x02;
246  if ( v.y() < -v.w() ) code |= 0x04;
247  if ( v.y() > v.w() ) code |= 0x08;
248  if ( v.z() < -v.w() ) code |= 0x10;
249  if ( v.z() > v.w() ) code |= 0x20;
250  return code;
251 }
252 
253 
264 bool Qgs3DUtils::isCullable( const QgsAABB &bbox, const QMatrix4x4 &viewProjectionMatrix )
265 {
266  uint out = 0xff;
267 
268  for ( int i = 0; i < 8; ++i )
269  {
270  QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
271  ( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
272  ( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );
273  QVector4D pc = viewProjectionMatrix * p;
274 
275  // if the logical AND of all the outcodes is non-zero then the BB is
276  // definitely outside the view frustum.
277  out = out & outcode( pc );
278  }
279  return out;
280 }
281 
283 {
284  return QgsVector3D( mapCoords.x() - origin.x(),
285  mapCoords.z() - origin.z(),
286  -( mapCoords.y() - origin.y() ) );
287 
288 }
289 
291 {
292  return QgsVector3D( worldCoords.x() + origin.x(),
293  -worldCoords.z() + origin.y(),
294  worldCoords.y() + origin.z() );
295 }
296 
298 {
299  QgsVector3D mapPoint1 = worldToMapCoordinates( worldPoint1, origin1 );
300  QgsVector3D mapPoint2 = mapPoint1;
301  if ( crs1 != crs2 )
302  {
303  // reproject if necessary
304  QgsCoordinateTransform ct( crs1, crs2, context );
305  try
306  {
307  QgsPointXY pt = ct.transform( QgsPointXY( mapPoint1.x(), mapPoint1.y() ) );
308  mapPoint2.set( pt.x(), pt.y(), mapPoint1.z() );
309  }
310  catch ( const QgsCsException & )
311  {
312  // bad luck, can't reproject for some reason
313  }
314  }
315  return mapToWorldCoordinates( mapPoint2, origin2 );
316 }
317 
static QString cullingModeToString(Qt3DRender::QCullFace::CullingMode mode)
Converts a value from CullingMode enum to a string.
Definition: qgs3dutils.cpp:85
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:29
3 Axis-aligned bounding box - in world coords.
Definition: qgsaabb.h:30
Z_final = z_terrain + z_geometry.
Definition: qgs3dutils.h:31
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 AltitudeBinding altBindingFromString(const QString &str)
Converts a string to a value from AltitudeBinding enum.
Definition: qgs3dutils.cpp:77
virtual float heightAt(double x, double y, const Qgs3DMapSettings &map) const
Returns height at (x,y) in terrain&#39;s CRS.
double zAt(int index) const
Returns the z-coordinate of the specified node in the line string.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
static void clampAltitudes(QgsLineString *lineString, AltitudeClamping altClamp, AltitudeBinding altBind, const QgsPoint &centroid, float height, const Qgs3DMapSettings &map)
Clamps altitude of vertices of a linestring according to the settings.
Definition: qgs3dutils.cpp:110
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
const QgsCurve * interiorRing(int i) const
float zMax
Definition: qgsaabb.h:80
Clamp just centroid of feature.
Definition: qgs3dutils.h:40
static Qt3DRender::QCullFace::CullingMode cullingModeFromString(const QString &str)
Converts a string to a value from CullingMode enum.
Definition: qgs3dutils.cpp:97
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
static bool isCullable(const QgsAABB &bbox, const QMatrix4x4 &viewProjectionMatrix)
Returns true if bbox is completely outside the current viewing volume.
Definition: qgs3dutils.cpp:264
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:45
3 Definition of the world
void set(double x, double y, double z)
Sets vector coordinates.
Definition: qgsvector3d.h:50
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
virtual QgsPoint centroid() const
Returns the centroid of the geometry.
Z_final = z_terrain.
Definition: qgs3dutils.h:32
float zMin
Definition: qgsaabb.h:77
int numInteriorRings() const
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:47
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:290
float yMax
Definition: qgsaabb.h:79
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
vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry...
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:282
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
Abstract base class for all geometries.
Contains information about the context in which a coordinate transform is executed.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
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:30
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
AltitudeClamping
how to handle altitude of vector features
Definition: qgs3dutils.h:28
float xMin
Definition: qgsaabb.h:75
static QString altBindingToString(AltitudeBinding altBind)
Converts a value from AltitudeBinding enum to a string.
Definition: qgs3dutils.cpp:66
void setX(double x)
Sets the x value of the point.
Definition: qgspointxy.h:104
double x
Definition: qgspointxy.h:47
static QList< QVector3D > positions(const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsFeatureRequest &req, AltitudeClamping altClamp)
Calculates (x,y,z) positions of a (multi)point in the Point vector layers.
Definition: qgs3dutils.cpp:188
Clamp every vertex of feature.
Definition: qgs3dutils.h:39
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
double terrainVerticalScale() const
Returns vertical scale (exaggeration) of terrain.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
float xMax
Definition: qgsaabb.h:78
static AltitudeClamping altClampingFromString(const QString &str)
Converts a string to a value from AltitudeClamping enum.
Definition: qgs3dutils.cpp:55
static QMatrix4x4 stringToMatrix4x4(const QString &str)
Convert a string to a 4x4 transform matrix.
Definition: qgs3dutils.cpp:178
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
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.
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.
Z_final = z_geometry.
Definition: qgs3dutils.h:30
double z
Definition: qgspoint.h:43
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
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:297
bool nextFeature(QgsFeature &f)
Polygon geometry type.
Definition: qgspolygon.h:31
const QgsCurve * exteriorRing() const
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
static QString altClampingToString(AltitudeClamping altClamp)
Converts a value from AltitudeClamping enum to a string.
Definition: qgs3dutils.cpp:43
Represents a vector layer which manages a vector based data sets.
static QString matrix4x4toString(const QMatrix4x4 &m)
Converts a 4x4 transform matrix to a string.
Definition: qgs3dutils.cpp:169
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AltitudeBinding
how to handle clamping of vertices of individual features
Definition: qgs3dutils.h:37
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:43
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data...
double x
Definition: qgspoint.h:41