QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgsmeshlayerutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmeshlayerutils.cpp
3  --------------------------
4  begin : August 2018
5  copyright : (C) 2018 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <limits>
19 #include <QTime>
20 #include <QDateTime>
21 
22 #include "qgsmeshlayerutils.h"
23 #include "qgsmeshtimesettings.h"
24 #include "qgstriangularmesh.h"
25 #include "qgsmeshdataprovider.h"
26 #include "qgsmesh3daveraging.h"
27 #include "qgsmeshlayer.h"
28 
29 
31 
32 QgsMeshDataBlock QgsMeshLayerUtils::datasetValues(
33  const QgsMeshLayer *meshLayer,
34  QgsMeshDatasetIndex index,
35  int valueIndex,
36  int count )
37 {
38  QgsMeshDataBlock block;
39  if ( !meshLayer )
40  return block;
41 
42  const QgsMeshDataProvider *provider = meshLayer->dataProvider();
43  if ( !provider )
44  return block;
45 
46  // try to get directly 2D dataset block
47  block = provider->datasetValues( index, valueIndex, count );
48  if ( block.isValid() )
49  return block;
50 
51  const QgsMesh3dAveragingMethod *averagingMethod = meshLayer->rendererSettings().averagingMethod();
52  // try to get 2D block
53  if ( !averagingMethod )
54  return block;
55 
56  QgsMesh3dDataBlock block3d = provider->dataset3dValues( index, valueIndex, count );
57  if ( !block3d.isValid() )
58  return block;
59 
60  block = averagingMethod->calculate( block3d );
61  return block;
62 }
63 
64 QVector<double> QgsMeshLayerUtils::calculateMagnitudes( const QgsMeshDataBlock &block )
65 {
66  Q_ASSERT( QgsMeshDataBlock::ActiveFlagInteger != block.type() );
67  int count = block.count();
68  QVector<double> ret( count );
69 
70  for ( int i = 0; i < count; ++i )
71  {
72  double mag = block.value( i ).scalar();
73  ret[i] = mag;
74  }
75  return ret;
76 }
77 
78 void QgsMeshLayerUtils::boundingBoxToScreenRectangle( const QgsMapToPixel &mtp,
79  const QSize &outputSize,
80  const QgsRectangle &bbox,
81  int &leftLim,
82  int &rightLim,
83  int &topLim,
84  int &bottomLim )
85 {
86  QgsPointXY ll = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
87  QgsPointXY ur = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
88  topLim = std::max( int( ur.y() ), 0 );
89  bottomLim = std::min( int( ll.y() ), outputSize.height() - 1 );
90  leftLim = std::max( int( ll.x() ), 0 );
91  rightLim = std::min( int( ur.x() ), outputSize.width() - 1 );
92 }
93 
94 static void lamTol( double &lam )
95 {
96  const static double eps = 1e-6;
97  if ( ( lam < 0.0 ) && ( lam > -eps ) )
98  {
99  lam = 0.0;
100  }
101 }
102 
103 static bool E3T_physicalToBarycentric( const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
104  double &lam1, double &lam2, double &lam3 )
105 {
106  if ( pA == pB || pA == pC || pB == pC )
107  return false; // this is not a valid triangle!
108 
109  // Compute vectors
110  QgsVector v0( pC - pA );
111  QgsVector v1( pB - pA );
112  QgsVector v2( pP - pA );
113 
114  // Compute dot products
115  double dot00 = v0 * v0;
116  double dot01 = v0 * v1;
117  double dot02 = v0 * v2;
118  double dot11 = v1 * v1;
119  double dot12 = v1 * v2;
120 
121  // Compute barycentric coordinates
122  double invDenom = 1.0 / ( dot00 * dot11 - dot01 * dot01 );
123  lam1 = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
124  lam2 = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
125  lam3 = 1.0 - lam1 - lam2;
126 
127  // Apply some tolerance to lam so we can detect correctly border points
128  lamTol( lam1 );
129  lamTol( lam2 );
130  lamTol( lam3 );
131 
132  // Return if POI is outside triangle
133  if ( ( lam1 < 0 ) || ( lam2 < 0 ) || ( lam3 < 0 ) )
134  {
135  return false;
136  }
137 
138  return true;
139 }
140 
141 double QgsMeshLayerUtils::interpolateFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
142  double val1, double val2, double val3, const QgsPointXY &pt )
143 {
144  double lam1, lam2, lam3;
145  if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
146  return std::numeric_limits<double>::quiet_NaN();
147 
148  return lam1 * val3 + lam2 * val2 + lam3 * val1;
149 }
150 
151 QgsVector QgsMeshLayerUtils::interpolateVectorFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, QgsVector vect1, QgsVector vect2, QgsVector vect3, const QgsPointXY &pt )
152 {
153  double lam1, lam2, lam3;
154  if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
155  return QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
156 
157  return vect3 * lam1 + vect2 * lam2 + vect1 * lam3;
158 }
159 
160 double QgsMeshLayerUtils::interpolateFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
161  double val, const QgsPointXY &pt )
162 {
163  double lam1, lam2, lam3;
164  if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
165  return std::numeric_limits<double>::quiet_NaN();
166 
167  return val;
168 }
169 
170 QgsVector QgsMeshLayerUtils::interpolateVectorFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
171  QgsVector vect, const QgsPointXY &pt )
172 {
173  double lam1, lam2, lam3;
174  if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
175  return QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
176 
177  return vect;
178 }
179 
180 
181 QVector<double> QgsMeshLayerUtils::interpolateFromFacesData(
182  QVector<double> valuesOnFaces,
183  const QgsMesh *nativeMesh,
184  const QgsTriangularMesh *triangularMesh,
185  QgsMeshDataBlock *active,
187 {
188  Q_UNUSED( triangularMesh )
189  Q_UNUSED( method )
190 
191  assert( nativeMesh );
193 
194  // assuming that native vertex count = triangular vertex count
195  assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );
196  int vertexCount = triangularMesh->vertices().size();
197 
198  QVector<double> res( vertexCount, 0.0 );
199  // for face datasets do simple average of the valid values of all faces that contains this vertex
200  QVector<int> count( vertexCount, 0 );
201 
202  for ( int i = 0; i < nativeMesh->faces.size(); ++i )
203  {
204  if ( !active || active->active( i ) )
205  {
206  double val = valuesOnFaces[ i ];
207  if ( !std::isnan( val ) )
208  {
209  // assign for all vertices
210  const QgsMeshFace &face = nativeMesh->faces.at( i );
211  for ( int j = 0; j < face.size(); ++j )
212  {
213  int vertexIndex = face[j];
214  res[vertexIndex] += val;
215  count[vertexIndex] += 1;
216  }
217  }
218  }
219  }
220 
221  for ( int i = 0; i < vertexCount; ++i )
222  {
223  if ( count.at( i ) > 0 )
224  {
225  res[i] = res[i] / double( count.at( i ) );
226  }
227  else
228  {
229  res[i] = std::numeric_limits<double>::quiet_NaN();
230  }
231  }
232 
233  return res;
234 }
235 
236 QgsRectangle QgsMeshLayerUtils::triangleBoundingBox( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3 )
237 {
238  // p1
239  double xMin = p1.x();
240  double xMax = p1.x();
241  double yMin = p1.y();
242  double yMax = p1.y();
243 
244  //p2
245  xMin = ( ( xMin < p2.x() ) ? xMin : p2.x() );
246  xMax = ( ( xMax > p2.x() ) ? xMax : p2.x() );
247  yMin = ( ( yMin < p2.y() ) ? yMin : p2.y() );
248  yMax = ( ( yMax > p2.y() ) ? yMax : p2.y() );
249 
250  // p3
251  xMin = ( ( xMin < p3.x() ) ? xMin : p3.x() );
252  xMax = ( ( xMax > p3.x() ) ? xMax : p3.x() );
253  yMin = ( ( yMin < p3.y() ) ? yMin : p3.y() );
254  yMax = ( ( yMax > p3.y() ) ? yMax : p3.y() );
255 
256  QgsRectangle bbox( xMin, yMin, xMax, yMax );
257  return bbox;
258 }
259 
260 QString QgsMeshLayerUtils::formatTime( double hours, const QgsMeshTimeSettings &settings )
261 {
262  QString ret;
263 
264  switch ( settings.providerTimeUnit() )
265  {
267  hours = hours / 3600.0;
268  break;
270  hours = hours / 60.0;
271  break;
273  break;
275  hours = hours * 24.0;
276  break;
277  }
278 
279  if ( settings.useAbsoluteTime() )
280  {
281  QString format( settings.absoluteTimeFormat() );
282  QDateTime dateTime( settings.absoluteTimeReferenceTime() );
283  qint64 seconds = static_cast<qint64>( hours * 3600.0 );
284  dateTime = dateTime.addSecs( seconds );
285  ret = dateTime.toString( format );
286  if ( ret.isEmpty() ) // error
287  ret = dateTime.toString();
288  }
289  else
290  {
291  QString format( settings.relativeTimeFormat() );
292  format = format.trimmed();
293  hours = hours + settings.relativeTimeOffsetHours();
294  int totalHours = static_cast<int>( hours );
295 
296  if ( format == QStringLiteral( "hh:mm:ss.zzz" ) )
297  {
298  int ms = static_cast<int>( hours * 3600.0 * 1000 );
299  int seconds = ms / 1000;
300  int z = ms % 1000;
301  int m = seconds / 60;
302  int s = seconds % 60;
303  int h = m / 60;
304  m = m % 60;
305  ret = QStringLiteral( "%1:%2:%3.%4" ).
306  arg( h, 2, 10, QLatin1Char( '0' ) ).
307  arg( m, 2, 10, QLatin1Char( '0' ) ).
308  arg( s, 2, 10, QLatin1Char( '0' ) ).
309  arg( z, 3, 10, QLatin1Char( '0' ) );
310  }
311  else if ( format == QStringLiteral( "hh:mm:ss" ) )
312  {
313  int seconds = static_cast<int>( hours * 3600.0 );
314  int m = seconds / 60;
315  int s = seconds % 60;
316  int h = m / 60;
317  m = m % 60;
318  ret = QStringLiteral( "%1:%2:%3" ).
319  arg( h, 2, 10, QLatin1Char( '0' ) ).
320  arg( m, 2, 10, QLatin1Char( '0' ) ).
321  arg( s, 2, 10, QLatin1Char( '0' ) );
322 
323  }
324  else if ( format == QStringLiteral( "d hh:mm:ss" ) )
325  {
326  int seconds = static_cast<int>( hours * 3600.0 );
327  int m = seconds / 60;
328  int s = seconds % 60;
329  int h = m / 60;
330  m = m % 60;
331  int d = totalHours / 24;
332  h = totalHours % 24;
333  ret = QStringLiteral( "%1 d %2:%3:%4" ).
334  arg( d ).
335  arg( h, 2, 10, QLatin1Char( '0' ) ).
336  arg( m, 2, 10, QLatin1Char( '0' ) ).
337  arg( s, 2, 10, QLatin1Char( '0' ) );
338  }
339  else if ( format == QStringLiteral( "d hh" ) )
340  {
341  int d = totalHours / 24;
342  int h = totalHours % 24;
343  ret = QStringLiteral( "%1 d %2" ).
344  arg( d ).
345  arg( h );
346  }
347  else if ( format == QStringLiteral( "d" ) )
348  {
349  int d = totalHours / 24;
350  ret = QStringLiteral( "%1" ).arg( d );
351  }
352  else if ( format == QStringLiteral( "ss" ) )
353  {
354  int seconds = static_cast<int>( hours * 3600.0 );
355  ret = QStringLiteral( "%1" ).arg( seconds );
356  }
357  else // "hh"
358  {
359  ret = QStringLiteral( "%1" ).arg( hours );
360  }
361  }
362  return ret;
363 }
364 
365 QDateTime QgsMeshLayerUtils::firstReferenceTime( QgsMeshLayer *meshLayer )
366 {
367  if ( !meshLayer )
368  return QDateTime();
369 
370  QgsMeshDataProvider *provider = meshLayer->dataProvider();
371 
372  if ( !provider )
373  return QDateTime();
374 
375  // Searches for the first valid reference time in the dataset groups
376  for ( int i = 0; i < provider->datasetGroupCount(); ++i )
377  {
379  if ( meta.referenceTime().isValid() )
380  return meta.referenceTime();
381  }
382 
383  return QDateTime();
384 }
385 
QDateTime referenceTime() const
Returns the reference time.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Triangular/Derived Mesh is mesh with vertices in map coordinates.
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
int count() const
Number of items stored in the block.
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e...
QgsMesh3dDataBlock is a block of 3d stacked mesh data related N faces defined on base mesh frame...
TimeUnit providerTimeUnit() const
Returns the provider time unit.
DataType type() const
Type of data stored in the block.
bool isValid() const
Whether the block is valid.
For each vertex does a simple average of values defined for all faces that contains given vertex...
QVector< QgsMeshVertex > vertices
vertices
double relativeTimeOffsetHours() const
Returns number of offset hours for relative time formatting.
bool active(int index) const
Returns a value for active flag by the index For scalar and vector 2d the behavior is undefined...
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
DataInterpolationMethod
Interpolation of value defined on vertices from datasets with data defined on faces.
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
QgsMeshDataBlock calculate(const QgsMesh3dDataBlock &block3d, QgsFeedback *feedback=nullptr) const
Calculated 2d block values from 3d stacked mesh values.
QString absoluteTimeFormat() const
Returns format used for absolute time.
Integer boolean flag whether face is active.
virtual QgsMeshDataBlock datasetValues(QgsMeshDatasetIndex index, int valueIndex, int count) const =0
Returns N vector/scalar values from the index from the dataset.
QgsMeshDataProvider * dataProvider() override
Returns the layer&#39;s data provider, it may be nullptr.
bool useAbsoluteTime() const
Returns whether to use absolute time format.
double scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
Represents a mesh time settings for mesh datasets.
double x
Definition: qgspointxy.h:47
A class to represent a vector.
Definition: qgsvector.h:29
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsMesh3dAveragingMethod * averagingMethod() const
Returns averaging method for conversion of 3d stacked mesh data to 2d data.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
Base class for providing data for QgsMeshLayer.
QDateTime absoluteTimeReferenceTime() const
Returns reference time used for absolute time format.
Mesh - vertices and faces.
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
bool isValid() const
Whether the block is valid.
QVector< int > QgsMeshFace
List of vertex indexes.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
Abstract class to interpolate 3d stacked mesh data to 2d data.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
virtual QgsMesh3dDataBlock dataset3dValues(QgsMeshDatasetIndex index, int faceIndex, int count) const =0
Returns N vector/scalar values from the face index from the dataset for 3d stacked meshes...
QgsMeshDatasetValue value(int index) const
Returns a value represented by the index For active flag the behavior is undefined.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:91
QVector< QgsMeshFace > faces
faces
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
QString relativeTimeFormat() const
Returns format used for relative time.
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.