QGIS API Documentation  3.23.0-Master (dd0cd13a00)
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 "qgslogger.h"
26 #include "qgsmeshdataprovider.h"
27 #include "qgsmesh3daveraging.h"
28 #include "qgsmeshlayer.h"
29 
30 
32 
33 int QgsMeshLayerUtils::datasetValuesCount( const QgsMesh *mesh, QgsMeshDatasetGroupMetadata::DataType dataType )
34 {
35  if ( !mesh )
36  return 0;
37 
38  switch ( dataType )
39  {
40  case QgsMeshDatasetGroupMetadata::DataType::DataOnEdges:
41  return mesh->edgeCount();
42  case QgsMeshDatasetGroupMetadata::DataType::DataOnFaces:
43  return mesh->faceCount();
44  case QgsMeshDatasetGroupMetadata::DataType::DataOnVertices:
45  return mesh->vertexCount();
46  case QgsMeshDatasetGroupMetadata::DataType::DataOnVolumes:
47  return mesh->faceCount(); // because they are averaged to faces
48  }
49  return 0;
50 }
51 
52 QgsMeshDatasetGroupMetadata::DataType QgsMeshLayerUtils::datasetValuesType( const QgsMeshDatasetGroupMetadata::DataType &type )
53 {
54  // data on volumes are averaged to 2D data on faces
55  if ( type == QgsMeshDatasetGroupMetadata::DataType::DataOnVolumes )
56  return QgsMeshDatasetGroupMetadata::DataType::DataOnFaces;
57 
58  return type;
59 }
60 
61 QgsMeshDataBlock QgsMeshLayerUtils::datasetValues(
62  const QgsMeshLayer *meshLayer,
63  QgsMeshDatasetIndex index,
64  int valueIndex,
65  int count )
66 {
67  QgsMeshDataBlock block;
68  if ( !meshLayer )
69  return block;
70 
71 
72  if ( !meshLayer->datasetCount( index ) )
73  return block;
74 
75  if ( !index.isValid() )
76  return block;
77 
78  const QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( index.group() );
79  if ( meta.dataType() != QgsMeshDatasetGroupMetadata::DataType::DataOnVolumes )
80  {
81  block = meshLayer->datasetValues( index, valueIndex, count );
82  if ( block.isValid() )
83  return block;
84  }
85  else
86  {
87  const QgsMesh3dAveragingMethod *averagingMethod = meshLayer->rendererSettings().averagingMethod();
88  if ( !averagingMethod )
89  return block;
90 
91  const QgsMesh3dDataBlock block3d = meshLayer->dataset3dValues( index, valueIndex, count );
92  if ( !block3d.isValid() )
93  return block;
94 
95  block = averagingMethod->calculate( block3d );
96  }
97  return block;
98 }
99 
100 QVector<QgsVector> QgsMeshLayerUtils::griddedVectorValues( const QgsMeshLayer *meshLayer,
101  const QgsMeshDatasetIndex index,
102  double xSpacing,
103  double ySpacing,
104  const QSize &size,
105  const QgsPointXY &minCorner )
106 {
107  QVector<QgsVector> vectors;
108 
109  if ( !meshLayer || !index.isValid() )
110  return vectors;
111 
112  const QgsTriangularMesh *triangularMesh = meshLayer->triangularMesh();
113  const QgsMesh *nativeMesh = meshLayer->nativeMesh();
114 
115  if ( !triangularMesh || !nativeMesh )
116  return vectors;
117 
118  const QgsMeshDatasetGroupMetadata meta = meshLayer->datasetGroupMetadata( index );
119  if ( !meta.isVector() )
120  return vectors;
121 
122  // extract vector dataset
123  const bool vectorDataOnVertices = meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
124  const int datacount = vectorDataOnVertices ? nativeMesh->vertices.count() : nativeMesh->faces.count();
125  const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues( meshLayer, index, 0, datacount );
126 
127  const QgsMeshDataBlock isFacesActive = meshLayer->areFacesActive( index, 0, nativeMesh->faceCount() );
128  const QgsMeshDatasetGroupMetadata::DataType dataType = meta.dataType();
129 
131  return vectors;
132 
133  try
134  {
135  vectors.reserve( size.height()*size.width() );
136  }
137  catch ( ... )
138  {
139  QgsDebugMsgLevel( "Unable to store the arrow grid in memory", 1 );
140  return QVector<QgsVector>();
141  }
142 
143  for ( int iy = 0; iy < size.height(); ++iy )
144  {
145  const double y = minCorner.y() + iy * ySpacing;
146  for ( int ix = 0; ix < size.width(); ++ix )
147  {
148  const double x = minCorner.x() + ix * xSpacing;
149  const QgsPoint point( x, y );
150  const int faceIndex = triangularMesh->faceIndexForPoint_v2( point );
151  int nativeFaceIndex = -1;
152  if ( faceIndex != -1 )
153  nativeFaceIndex = triangularMesh->trianglesToNativeFaces().at( faceIndex );
154  QgsMeshDatasetValue value;
155  if ( nativeFaceIndex != -1 && isFacesActive.active( nativeFaceIndex ) )
156  {
157  switch ( dataType )
158  {
161  value = vals.value( nativeFaceIndex );
162  break;
164  {
165  const QgsMeshFace &face = triangularMesh->triangles()[faceIndex];
166  const int v1 = face[0], v2 = face[1], v3 = face[2];
167  const QgsPoint p1 = triangularMesh->vertices()[v1], p2 = triangularMesh->vertices()[v2], p3 = triangularMesh->vertices()[v3];
168  const QgsMeshDatasetValue val1 = vals.value( v1 );
169  const QgsMeshDatasetValue val2 = vals.value( v2 );
170  const QgsMeshDatasetValue val3 = vals.value( v3 );
171  const double x = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.x(), val2.x(), val3.x(), point );
172  const double y = QgsMeshLayerUtils::interpolateFromVerticesData( p1, p2, p3, val1.y(), val2.y(), val3.y(), point );
173  value = QgsMeshDatasetValue( x, y );
174  }
175  break;
177  break;
178  }
179  }
180  vectors.append( QgsVector( value.x(), value.y() ) );
181  }
182  }
183  return vectors;
184 }
185 
186 QVector<double> QgsMeshLayerUtils::calculateMagnitudes( const QgsMeshDataBlock &block )
187 {
188  Q_ASSERT( QgsMeshDataBlock::ActiveFlagInteger != block.type() );
189  const int count = block.count();
190  QVector<double> ret( count );
191 
192  for ( int i = 0; i < count; ++i )
193  {
194  const double mag = block.value( i ).scalar();
195  ret[i] = mag;
196  }
197  return ret;
198 }
199 
200 QgsRectangle QgsMeshLayerUtils::boundingBoxToScreenRectangle(
201  const QgsMapToPixel &mtp,
202  const QgsRectangle &bbox
203 )
204 {
205  const QgsPointXY topLeft = mtp.transform( bbox.xMinimum(), bbox.yMaximum() );
206  const QgsPointXY topRight = mtp.transform( bbox.xMaximum(), bbox.yMaximum() );
207  const QgsPointXY bottomLeft = mtp.transform( bbox.xMinimum(), bbox.yMinimum() );
208  const QgsPointXY bottomRight = mtp.transform( bbox.xMaximum(), bbox.yMinimum() );
209 
210  const double xMin = std::min( {topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()} );
211  const double xMax = std::max( {topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()} );
212  const double yMin = std::min( {topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()} );
213  const double yMax = std::max( {topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()} );
214 
215  const QgsRectangle ret( xMin, yMin, xMax, yMax );
216  return ret;
217 }
218 
219 void QgsMeshLayerUtils::boundingBoxToScreenRectangle(
220  const QgsMapToPixel &mtp,
221  const QSize &outputSize,
222  const QgsRectangle &bbox,
223  int &leftLim,
224  int &rightLim,
225  int &bottomLim,
226  int &topLim )
227 {
228  const QgsRectangle screenBBox = boundingBoxToScreenRectangle( mtp, bbox );
229 
230  bottomLim = std::max( int( screenBBox.yMinimum() ), 0 );
231  topLim = std::min( int( screenBBox.yMaximum() ), outputSize.height() - 1 );
232  leftLim = std::max( int( screenBBox.xMinimum() ), 0 );
233  rightLim = std::min( int( screenBBox.xMaximum() ), outputSize.width() - 1 );
234 }
235 
236 static void lamTol( double &lam )
237 {
238  const static double eps = 1e-6;
239  if ( ( lam < 0.0 ) && ( lam > -eps ) )
240  {
241  lam = 0.0;
242  }
243 }
244 
245 static bool E3T_physicalToBarycentric( const QgsPointXY &pA, const QgsPointXY &pB, const QgsPointXY &pC, const QgsPointXY &pP,
246  double &lam1, double &lam2, double &lam3 )
247 {
248  // Compute vectors
249  const double xa = pA.x();
250  const double ya = pA.y();
251  const double v0x = pC.x() - xa ;
252  const double v0y = pC.y() - ya ;
253  const double v1x = pB.x() - xa ;
254  const double v1y = pB.y() - ya ;
255  const double v2x = pP.x() - xa ;
256  const double v2y = pP.y() - ya ;
257 
258  // Compute dot products
259  const double dot00 = v0x * v0x + v0y * v0y;
260  const double dot01 = v0x * v1x + v0y * v1y;
261  const double dot02 = v0x * v2x + v0y * v2y;
262  const double dot11 = v1x * v1x + v1y * v1y;
263  const double dot12 = v1x * v2x + v1y * v2y;
264 
265  // Compute barycentric coordinates
266  double invDenom = dot00 * dot11 - dot01 * dot01;
267  if ( invDenom == 0 )
268  return false;
269  invDenom = 1.0 / invDenom;
270  lam1 = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
271  lam2 = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
272  lam3 = 1.0 - lam1 - lam2;
273 
274  // Apply some tolerance to lam so we can detect correctly border points
275  lamTol( lam1 );
276  lamTol( lam2 );
277  lamTol( lam3 );
278 
279  // Return if POI is outside triangle
280  if ( ( lam1 < 0 ) || ( lam2 < 0 ) || ( lam3 < 0 ) )
281  {
282  return false;
283  }
284 
285  return true;
286 }
287 
288 double QgsMeshLayerUtils::interpolateFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
289  double val1, double val2, double val3, const QgsPointXY &pt )
290 {
291  double lam1, lam2, lam3;
292  if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
293  return std::numeric_limits<double>::quiet_NaN();
294 
295  return lam1 * val3 + lam2 * val2 + lam3 * val1;
296 }
297 
298 double QgsMeshLayerUtils::interpolateFromVerticesData( double fraction, double val1, double val2 )
299 {
300  if ( std::isnan( val1 ) || std::isnan( val2 ) || ( fraction < 0 ) || ( fraction > 1 ) )
301  {
302  return std::numeric_limits<double>::quiet_NaN();
303  }
304  return val1 + ( val2 - val1 ) * fraction;
305 }
306 
307 QgsMeshDatasetValue QgsMeshLayerUtils::interpolateFromVerticesData( double fraction, const QgsMeshDatasetValue &val1, const QgsMeshDatasetValue &val2 )
308 {
309  return QgsMeshDatasetValue( interpolateFromVerticesData( fraction, val1.x(), val2.x() ),
310  interpolateFromVerticesData( fraction, val1.y(), val2.y() ) );
311 }
312 
313 
314 QgsVector QgsMeshLayerUtils::interpolateVectorFromVerticesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3, QgsVector vect1, QgsVector vect2, QgsVector vect3, const QgsPointXY &pt )
315 {
316  double lam1, lam2, lam3;
317  if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
318  return QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
319 
320  return vect3 * lam1 + vect2 * lam2 + vect1 * lam3;
321 }
322 
323 double QgsMeshLayerUtils::interpolateFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
324  double val, const QgsPointXY &pt )
325 {
326  double lam1, lam2, lam3;
327  if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
328  return std::numeric_limits<double>::quiet_NaN();
329 
330  return val;
331 }
332 
333 QgsVector QgsMeshLayerUtils::interpolateVectorFromFacesData( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3,
334  QgsVector vect, const QgsPointXY &pt )
335 {
336  double lam1, lam2, lam3;
337  if ( !E3T_physicalToBarycentric( p1, p2, p3, pt, lam1, lam2, lam3 ) )
338  return QgsVector( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
339 
340  return vect;
341 }
342 
343 
344 QVector<double> QgsMeshLayerUtils::interpolateFromFacesData(
345  QVector<double> valuesOnFaces,
346  const QgsMesh *nativeMesh,
347  const QgsTriangularMesh *triangularMesh,
348  QgsMeshDataBlock *active,
350 {
351  assert( nativeMesh );
352  Q_UNUSED( method );
353  Q_UNUSED( triangularMesh );
354 
355  // assuming that native vertex count = triangular vertex count
356  assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );
357 
358  return interpolateFromFacesData( valuesOnFaces, *nativeMesh, active, method );
359 }
360 
361 QVector<double> QgsMeshLayerUtils::interpolateFromFacesData( const QVector<double> &valuesOnFaces,
362  const QgsMesh &nativeMesh,
363  QgsMeshDataBlock *active,
365 {
366 
367  QgsMeshDataBlock activeFace;
368  if ( active )
369  activeFace = *active;
370  else
371  {
372  activeFace = QgsMeshDataBlock( QgsMeshDataBlock::ActiveFlagInteger, nativeMesh.faceCount() );
373  activeFace.setValid( true );
374  }
375 
376  return interpolateFromFacesData( valuesOnFaces, nativeMesh, activeFace, method );
377 
378 }
379 
380 QVector<double> QgsMeshLayerUtils::interpolateFromFacesData( const QVector<double> &valuesOnFaces, const QgsMesh &nativeMesh, const QgsMeshDataBlock &active, QgsMeshRendererScalarSettings::DataResamplingMethod method )
381 {
382  const int vertexCount = nativeMesh.vertexCount();
383  Q_UNUSED( method );
385 
386  QVector<double> res( vertexCount, 0.0 );
387  // for face datasets do simple average of the valid values of all faces that contains this vertex
388  QVector<int> count( vertexCount, 0 );
389 
390  for ( int i = 0; i < nativeMesh.faceCount(); ++i )
391  {
392  if ( active.active( i ) )
393  {
394  const double val = valuesOnFaces[ i ];
395  if ( !std::isnan( val ) )
396  {
397  // assign for all vertices
398  const QgsMeshFace &face = nativeMesh.faces.at( i );
399  for ( int j = 0; j < face.size(); ++j )
400  {
401  const int vertexIndex = face[j];
402  res[vertexIndex] += val;
403  count[vertexIndex] += 1;
404  }
405  }
406  }
407  }
408 
409  for ( int i = 0; i < vertexCount; ++i )
410  {
411  if ( count.at( i ) > 0 )
412  {
413  res[i] = res[i] / double( count.at( i ) );
414  }
415  else
416  {
417  res[i] = std::numeric_limits<double>::quiet_NaN();
418  }
419  }
420 
421  return res;
422 }
423 
424 QVector<double> QgsMeshLayerUtils::resampleFromVerticesToFaces(
425  const QVector<double> valuesOnVertices,
426  const QgsMesh *nativeMesh,
427  const QgsTriangularMesh *triangularMesh,
428  const QgsMeshDataBlock *active,
430 {
431  assert( nativeMesh );
432  Q_UNUSED( method );
434 
435  // assuming that native vertex count = triangular vertex count
436  Q_UNUSED( triangularMesh );
437  assert( nativeMesh->vertices.size() == triangularMesh->vertices().size() );
438 
439  QVector<double> ret( nativeMesh->faceCount(), std::numeric_limits<double>::quiet_NaN() );
440 
441  for ( int i = 0; i < nativeMesh->faces.size(); ++i )
442  {
443  const QgsMeshFace face = nativeMesh->face( i );
444  if ( active->active( i ) && face.count() > 2 )
445  {
446  double value = 0;
447  for ( int j = 0; j < face.count(); ++j )
448  {
449  value += valuesOnVertices.at( face.at( j ) );
450  }
451  ret[i] = value / face.count();
452  }
453  }
454 
455  return ret;
456 }
457 
458 QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMeshLayer *meshLayer,
459  const QgsMeshDatasetIndex index,
460  QgsMeshDataBlock *activeFaceFlagValues,
462 {
463  QVector<double> ret;
464 
465  if ( !meshLayer && !index.isValid() )
466  return ret;
467 
468  const QgsTriangularMesh *triangularMesh = meshLayer->triangularMesh();
469  const QgsMesh *nativeMesh = meshLayer->nativeMesh();
470  if ( !triangularMesh || !nativeMesh )
471  return ret;
472 
473  const QgsMeshDatasetGroupMetadata metadata = meshLayer->datasetGroupMetadata( index );
474  const bool scalarDataOnVertices = metadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
475 
476  // populate scalar values
477  const int datacount = scalarDataOnVertices ? nativeMesh->vertices.count() : nativeMesh->faces.count();
478  const QgsMeshDataBlock vals = QgsMeshLayerUtils::datasetValues(
479  meshLayer,
480  index,
481  0,
482  datacount );
483 
484  QgsMeshDataBlock activeFace;
485  if ( !activeFaceFlagValues )
486  {
488  activeFace.setValid( true );
489  }
490  else
491  activeFace = *activeFaceFlagValues;
492 
493  return calculateMagnitudeOnVertices( *nativeMesh, metadata, vals, activeFace, method );
494 }
495 
496 QVector<double> QgsMeshLayerUtils::calculateMagnitudeOnVertices( const QgsMesh &nativeMesh,
497  const QgsMeshDatasetGroupMetadata &groupMetadata,
498  const QgsMeshDataBlock &datasetValues,
499  const QgsMeshDataBlock &activeFaceFlagValues,
501 {
502  QVector<double> ret;
503  const bool scalarDataOnVertices = groupMetadata.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices;
504 
505  if ( datasetValues.isValid() )
506  {
507  ret = QgsMeshLayerUtils::calculateMagnitudes( datasetValues );
508 
509  if ( !scalarDataOnVertices )
510  {
511  //Need to interpolate data on vertices
512  ret = QgsMeshLayerUtils::interpolateFromFacesData(
513  ret,
514  nativeMesh,
515  activeFaceFlagValues,
516  method );
517  }
518  }
519  return ret;
520 }
521 
522 QgsRectangle QgsMeshLayerUtils::triangleBoundingBox( const QgsPointXY &p1, const QgsPointXY &p2, const QgsPointXY &p3 )
523 {
524  // p1
525  double xMin = p1.x();
526  double xMax = p1.x();
527  double yMin = p1.y();
528  double yMax = p1.y();
529 
530  //p2
531  xMin = ( ( xMin < p2.x() ) ? xMin : p2.x() );
532  xMax = ( ( xMax > p2.x() ) ? xMax : p2.x() );
533  yMin = ( ( yMin < p2.y() ) ? yMin : p2.y() );
534  yMax = ( ( yMax > p2.y() ) ? yMax : p2.y() );
535 
536  // p3
537  xMin = ( ( xMin < p3.x() ) ? xMin : p3.x() );
538  xMax = ( ( xMax > p3.x() ) ? xMax : p3.x() );
539  yMin = ( ( yMin < p3.y() ) ? yMin : p3.y() );
540  yMax = ( ( yMax > p3.y() ) ? yMax : p3.y() );
541 
542  const QgsRectangle bbox( xMin, yMin, xMax, yMax );
543  return bbox;
544 }
545 
546 QString QgsMeshLayerUtils::formatTime( double hours, const QDateTime &referenceTime, const QgsMeshTimeSettings &settings )
547 {
548  QString ret;
549 
550  if ( referenceTime.isValid() )
551  {
552  const QString format( settings.absoluteTimeFormat() );
553  QDateTime dateTime( referenceTime );
554  const qint64 seconds = static_cast<qint64>( hours * 3600.0 );
555  dateTime = dateTime.addSecs( seconds );
556  ret = dateTime.toString( format );
557  if ( ret.isEmpty() ) // error
558  ret = dateTime.toString();
559  }
560  else
561  {
562  QString format( settings.relativeTimeFormat() );
563  format = format.trimmed();
564  const int totalHours = static_cast<int>( hours );
565 
566  if ( format == QLatin1String( "hh:mm:ss.zzz" ) )
567  {
568  const int ms = static_cast<int>( hours * 3600.0 * 1000 );
569  const int seconds = ms / 1000;
570  const int z = ms % 1000;
571  int m = seconds / 60;
572  const int s = seconds % 60;
573  const int h = m / 60;
574  m = m % 60;
575  ret = QStringLiteral( "%1:%2:%3.%4" ).
576  arg( h, 2, 10, QLatin1Char( '0' ) ).
577  arg( m, 2, 10, QLatin1Char( '0' ) ).
578  arg( s, 2, 10, QLatin1Char( '0' ) ).
579  arg( z, 3, 10, QLatin1Char( '0' ) );
580  }
581  else if ( format == QLatin1String( "hh:mm:ss" ) )
582  {
583  const int seconds = static_cast<int>( hours * 3600.0 );
584  int m = seconds / 60;
585  const int s = seconds % 60;
586  const int h = m / 60;
587  m = m % 60;
588  ret = QStringLiteral( "%1:%2:%3" ).
589  arg( h, 2, 10, QLatin1Char( '0' ) ).
590  arg( m, 2, 10, QLatin1Char( '0' ) ).
591  arg( s, 2, 10, QLatin1Char( '0' ) );
592 
593  }
594  else if ( format == QLatin1String( "d hh:mm:ss" ) )
595  {
596  const int seconds = static_cast<int>( hours * 3600.0 );
597  int m = seconds / 60;
598  const int s = seconds % 60;
599  int h = m / 60;
600  m = m % 60;
601  const int d = totalHours / 24;
602  h = totalHours % 24;
603  ret = QStringLiteral( "%1 d %2:%3:%4" ).
604  arg( d ).
605  arg( h, 2, 10, QLatin1Char( '0' ) ).
606  arg( m, 2, 10, QLatin1Char( '0' ) ).
607  arg( s, 2, 10, QLatin1Char( '0' ) );
608  }
609  else if ( format == QLatin1String( "d hh" ) )
610  {
611  const int d = totalHours / 24;
612  const int h = totalHours % 24;
613  ret = QStringLiteral( "%1 d %2" ).
614  arg( d ).
615  arg( h );
616  }
617  else if ( format == QLatin1String( "d" ) )
618  {
619  const int d = totalHours / 24;
620  ret = QString::number( d );
621  }
622  else if ( format == QLatin1String( "ss" ) )
623  {
624  const int seconds = static_cast<int>( hours * 3600.0 );
625  ret = QString::number( seconds );
626  }
627  else // "hh"
628  {
629  ret = QString::number( hours );
630  }
631  }
632  return ret;
633 }
634 
635 QVector<QVector3D> QgsMeshLayerUtils::calculateNormals( const QgsTriangularMesh &triangularMesh, const QVector<double> &verticalMagnitude, bool isRelative )
636 {
637  QVector<QVector3D> normals( triangularMesh.vertices().count() );
638  for ( const auto &face : triangularMesh.triangles() )
639  {
640  for ( int i = 0; i < 3; i++ )
641  {
642  const int index( face.at( i ) );
643  const int index1( face.at( ( i + 1 ) % 3 ) );
644  const int index2( face.at( ( i + 2 ) % 3 ) );
645 
646  const QgsMeshVertex &vert( triangularMesh.vertices().at( index ) );
647  const QgsMeshVertex &otherVert1( triangularMesh.vertices().at( index1 ) );
648  const QgsMeshVertex &otherVert2( triangularMesh.vertices().at( index2 ) );
649 
650  float adjustRelative = 0;
651  float adjustRelative1 = 0;
652  float adjustRelative2 = 0;
653 
654  if ( isRelative )
655  {
656  adjustRelative = vert.z();
657  adjustRelative1 = otherVert1.z();
658  adjustRelative2 = otherVert2.z();
659  }
660 
661  const QVector3D v1( float( otherVert1.x() - vert.x() ),
662  float( otherVert1.y() - vert.y() ),
663  float( verticalMagnitude[index1] - verticalMagnitude[index] + adjustRelative1 - adjustRelative ) );
664  const QVector3D v2( float( otherVert2.x() - vert.x() ),
665  float( otherVert2.y() - vert.y() ),
666  float( verticalMagnitude[index2] - verticalMagnitude[index] + adjustRelative2 - adjustRelative ) );
667 
668  normals[index] += QVector3D::crossProduct( v1, v2 );
669  }
670  }
671 
672  return normals;
673 }
674 
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:90
Abstract class to interpolate 3d stacked mesh data to 2d data.
QgsMeshDataBlock calculate(const QgsMesh3dDataBlock &block3d, QgsFeedback *feedback=nullptr) const
Calculated 2d block values from 3d stacked mesh values.
QgsMesh3dDataBlock is a block of 3d stacked mesh data related N faces defined on base mesh frame.
bool isValid() const
Whether the block is valid.
QgsMeshDataBlock is a block of integers/doubles that can be used to retrieve: active flags (e....
QgsMeshDatasetValue value(int index) const
Returns a value represented by the index For active flag the behavior is undefined.
bool isValid() const
Whether the block is valid.
DataType type() const
Type of data stored in the block.
@ ActiveFlagInteger
Integer boolean flag whether face is active.
bool active(int index) const
Returns a value for active flag by the index For scalar and vector 2d the behavior is undefined.
int count() const
Number of items stored in the block.
void setValid(bool valid)
Sets block validity.
QgsMeshDatasetGroupMetadata is a collection of dataset group metadata such as whether the data is vec...
bool isVector() const
Returns whether dataset group has vector data.
DataType dataType() const
Returns whether dataset group data is defined on vertices or faces or volumes.
DataType
Location of where data is specified for datasets in the dataset group.
@ DataOnEdges
Data is defined on edges.
@ DataOnFaces
Data is defined on faces.
@ DataOnVertices
Data is defined on vertices.
@ DataOnVolumes
Data is defined on volumes.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
bool isValid() const
Returns whether index is valid, ie at least groups is set.
int group() const
Returns a group index.
QgsMeshDatasetValue represents single dataset value.
double y() const
Returns y value.
double scalar() const
Returns magnitude of vector for vector data or scalar value for scalar data.
double x() const
Returns x value.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:97
int datasetCount(const QgsMeshDatasetIndex &index) const
Returns the dataset count in the dataset groups.
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
QgsMesh3dDataBlock dataset3dValues(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns N vector/scalar values from the face index from the dataset for 3d stacked meshes.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
QgsMeshDataBlock datasetValues(const QgsMeshDatasetIndex &index, int valueIndex, int count) const
Returns N vector/scalar values from the index from the dataset.
QgsMeshDataBlock areFacesActive(const QgsMeshDatasetIndex &index, int faceIndex, int count) const
Returns whether the faces are active for particular dataset.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
QgsMeshDatasetGroupMetadata datasetGroupMetadata(const QgsMeshDatasetIndex &index) const
Returns the dataset groups metadata.
DataResamplingMethod
Resampling of value from dataset.
@ NeighbourAverage
Does a simple average of values defined for all surrounding faces/vertices.
QgsMesh3dAveragingMethod * averagingMethod() const
Returns averaging method for conversion of 3d stacked mesh data to 2d data.
Represents a mesh time settings for mesh datasets.
QString relativeTimeFormat() const
Returns format used for relative time.
QString absoluteTimeFormat() const
Returns format used for absolute time.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
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
Triangular/Derived Mesh is mesh with vertices in map coordinates.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original faces.
int faceIndexForPoint_v2(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing and don't use geos to be faster.
A class to represent a vector.
Definition: qgsvector.h:30
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QVector< int > QgsMeshFace
List of vertex indexes.
Mesh - vertices, edges and faces.
int vertexCount() const
Returns number of vertices.
QVector< QgsMeshVertex > vertices
QgsMeshFace face(int index) const
Returns a face at the index.
QVector< QgsMeshFace > faces
int faceCount() const
Returns number of faces.
int edgeCount() const
Returns number of edge.