QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgsmeshcalcutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmeshcalcutils.cpp
3  --------------------
4  begin : December 18th, 2018
5  copyright : (C) 2018 by Peter Petrik
6  email : zilolv 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  ***************************************************************************/
18 
19 #include <QFileInfo>
20 
21 #include "qgsmeshcalcnode.h"
22 #include "qgsmeshcalcutils.h"
23 #include "qgsmeshmemorydataprovider.h"
24 #include "qgstriangularmesh.h"
25 #include "qgsmapsettings.h"
26 #include "qgsmeshlayerutils.h"
27 #include "qgsmeshlayerrenderer.h"
28 
29 const double D_TRUE = 1.0;
30 const double D_FALSE = 0.0;
31 const double D_NODATA = std::numeric_limits<double>::quiet_NaN();
32 
33 std::shared_ptr<QgsMeshMemoryDatasetGroup> QgsMeshCalcUtils::create( const QString &datasetGroupName ) const
34 {
35  const auto dp = mMeshLayer->dataProvider();
36  std::shared_ptr<QgsMeshMemoryDatasetGroup> grp;
37  for ( int groupIndex = 0; groupIndex < dp->datasetGroupCount(); ++groupIndex )
38  {
39  const auto meta = dp->datasetGroupMetadata( groupIndex );
40  const QString name = meta.name();
41  if ( name == datasetGroupName )
42  {
43  // we need to convert the native type to the requested type
44  // only possibility that cannot happen is to convert vertex dataset to
45  // face dataset. This would suggest bug in determineResultDataType()
46  Q_ASSERT( !( ( meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices ) && ( mOutputType == QgsMeshDatasetGroupMetadata::DataOnFaces ) ) );
47 
48  grp = std::make_shared<QgsMeshMemoryDatasetGroup>();
49  grp->isScalar = meta.isScalar();
50  grp->type = mOutputType;
51  grp->maximum = meta.maximum();
52  grp->minimum = meta.minimum();
53  grp->name = meta.name();
54 
55  int nativeCount = ( meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices ) ? dp->vertexCount() : dp->faceCount();
56  int resultCount = ( mOutputType == QgsMeshDatasetGroupMetadata::DataOnVertices ) ? dp->vertexCount() : dp->faceCount();
57 
58  for ( int datasetIndex = 0; datasetIndex < dp->datasetCount( groupIndex ); ++datasetIndex )
59  {
60  const QgsMeshDatasetIndex index( groupIndex, datasetIndex );
61  const auto dsMeta = dp->datasetMetadata( index );
62  std::shared_ptr<QgsMeshMemoryDataset> ds = create( grp->type );
63  ds->maximum = dsMeta.maximum();
64  ds->minimum = dsMeta.minimum();
65  ds->time = dsMeta.time();
66  ds->valid = dsMeta.isValid();
67 
68  // the function already averages volume datasets to face dataset values
69  QgsMeshDataBlock block = QgsMeshLayerUtils::datasetValues( mMeshLayer, index, 0, nativeCount );
70  // it is 2D memory datasets, so it shouldn't be invalid
71  Q_ASSERT( block.isValid() );
72  Q_ASSERT( block.count() == nativeCount );
73 
74  // for data on faces, there could be request to interpolate the data to vertices
75  if ( ( meta.dataType() != QgsMeshDatasetGroupMetadata::DataOnVertices ) && ( mOutputType == QgsMeshDatasetGroupMetadata::DataOnVertices ) )
76  {
77 
78  if ( grp->isScalar )
79  {
80  QVector<double> data =
81  QgsMeshLayerUtils::interpolateFromFacesData(
82  block.values(),
83  nativeMesh(),
84  triangularMesh(),
85  nullptr,
87  );
88  Q_ASSERT( data.size() == resultCount );
89  for ( int valueIndex = 0; valueIndex < resultCount; ++valueIndex )
90  ds->values[valueIndex] = QgsMeshDatasetValue( data[valueIndex] );
91  }
92  else
93  {
94  QVector<double> buf = block.values();
95  QVector<double> x( nativeCount );
96  QVector<double> y( nativeCount );
97  for ( int value_i = 0; value_i < nativeCount; ++value_i )
98  {
99  x[value_i] = buf[2 * value_i];
100  y[value_i] = buf[2 * value_i + 1];
101  }
102 
103  QVector<double> dataX =
104  QgsMeshLayerUtils::interpolateFromFacesData(
105  x,
106  mMeshLayer->nativeMesh(),
107  mMeshLayer->triangularMesh(),
108  nullptr,
109  mMeshLayer->rendererSettings().scalarSettings( groupIndex ).dataInterpolationMethod()
110  );
111  Q_ASSERT( dataX.size() == resultCount );
112  QVector<double> dataY =
113  QgsMeshLayerUtils::interpolateFromFacesData(
114  y,
115  mMeshLayer->nativeMesh(),
116  mMeshLayer->triangularMesh(),
117  nullptr,
118  mMeshLayer->rendererSettings().scalarSettings( groupIndex ).dataInterpolationMethod()
119  );
120 
121  Q_ASSERT( dataY.size() == resultCount );
122 
123  for ( int value_i = 0; value_i < resultCount; ++value_i )
124  {
125  ds->values[value_i] = QgsMeshDatasetValue( dataX[value_i], dataY[value_i] );
126  }
127  }
128  }
129  else
130  {
131  for ( int value_i = 0; value_i < resultCount; ++value_i )
132  ds->values[value_i] = block.value( value_i );
133  }
134 
136  {
137  const QgsMeshDataBlock active = dp->areFacesActive( index, 0, dp->faceCount() );
138  Q_ASSERT( active.count() == dp->faceCount() );
139  for ( int value_i = 0; value_i < dp->faceCount(); ++value_i )
140  ds->active[value_i] = active.active( value_i );
141  }
142  grp->addDataset( ds );
143  }
144 
145  break;
146  }
147  }
148  return grp;
149 }
150 
151 std::shared_ptr<QgsMeshMemoryDataset> QgsMeshCalcUtils::create( const QgsMeshMemoryDatasetGroup &grp ) const
152 {
153  return create( grp.type );
154 }
155 
156 std::shared_ptr<QgsMeshMemoryDataset> QgsMeshCalcUtils::create( const QgsMeshDatasetGroupMetadata::DataType type ) const
157 {
158  Q_ASSERT( type != QgsMeshDatasetGroupMetadata::DataOnVolumes );
159 
160  std::shared_ptr<QgsMeshMemoryDataset> ds = std::make_shared<QgsMeshMemoryDataset>();
162  {
163  ds->values.resize( mMeshLayer->dataProvider()->vertexCount() );
164  ds->active.resize( mMeshLayer->dataProvider()->faceCount() );
165  memset( ds->active.data(), 1, static_cast<size_t>( ds->active.size() ) * sizeof( int ) );
166  }
167  else
168  {
169  ds->values.resize( mMeshLayer->dataProvider()->faceCount() );
170  }
171  ds->valid = true;
172  return ds;
173 }
174 
175 QgsMeshCalcUtils:: QgsMeshCalcUtils( QgsMeshLayer *layer,
176  const QStringList &usedGroupNames,
177  double startTime,
178  double endTime )
179  : mMeshLayer( layer )
180  , mIsValid( false )
181 {
182  // Resolve output type of the calculation
183  mOutputType = determineResultDataType( layer, usedGroupNames );
184 
185  // First populate group's names map and see if we have all groups present
186  // And basically fetch all data from any mesh provider to memory
187  for ( const QString &groupName : usedGroupNames )
188  {
189  std::shared_ptr<QgsMeshMemoryDatasetGroup> ds = create( groupName );
190  if ( !ds )
191  return;
192 
193  mDatasetGroupMap.insert( groupName, ds );
194  }
195 
196  // Now populate used times and check that all datasets do have some times
197  // OR just one time (== one output)
198  bool timesPopulated = false;
199  const auto vals = mDatasetGroupMap.values();
200  for ( const auto &ds : vals )
201  {
202  if ( ds->datasetCount() == 0 )
203  {
204  // dataset must have at least 1 output
205  return;
206  }
207 
208  if ( ds->datasetCount() > 1 )
209  {
210  if ( timesPopulated )
211  {
212  if ( ds->datasetCount() != mTimes.size() )
213  {
214  // different number of datasets in the groupss
215  return;
216  }
217  }
218 
219  for ( int datasetIndex = 0; datasetIndex < ds->datasetCount(); ++datasetIndex )
220  {
221  std::shared_ptr<const QgsMeshMemoryDataset> o = ds->constDataset( datasetIndex );
222  if ( timesPopulated )
223  {
224  if ( !qgsDoubleNear( mTimes[datasetIndex], o->time ) )
225  {
226  // error, the times in different datasets differ
227  return;
228  }
229  }
230  else
231  {
232  mTimes.append( o->time );
233  }
234  }
235 
236  timesPopulated = true;
237  }
238  }
239 
240  // case of all group are not time varying or usedGroupNames is empty
241  if ( mTimes.isEmpty() )
242  {
243  mTimes.push_back( 0.0 );
244  }
245  else
246  {
247  // filter out times we do not need to speed up calculations
248  for ( QVector<double>::iterator it = mTimes.begin(); it != mTimes.end(); )
249  {
250  if ( qgsDoubleNear( *it, startTime ) ||
251  qgsDoubleNear( *it, endTime ) ||
252  ( ( *it >= startTime ) && ( *it <= endTime ) ) )
253  ++it;
254  else
255  it = mTimes.erase( it );
256  }
257  }
258 
259  // check that all datasets are of the same type
260  for ( const auto &ds : vals )
261  {
262  if ( ds->type != mOutputType )
263  return;
264  }
265 
266  // All is valid!
267  mIsValid = true;
268 }
269 
270 bool QgsMeshCalcUtils::isValid() const
271 {
272  return mIsValid;
273 }
274 
275 const QgsMeshLayer *QgsMeshCalcUtils::layer() const
276 {
277  return mMeshLayer;
278 }
279 
280 std::shared_ptr<const QgsMeshMemoryDatasetGroup> QgsMeshCalcUtils::group( const QString &datasetName ) const
281 {
282  return mDatasetGroupMap[datasetName];
283 }
284 
285 void QgsMeshCalcUtils::populateSpatialFilter( QgsMeshMemoryDatasetGroup &filter, const QgsRectangle &extent ) const
286 {
287  Q_ASSERT( mOutputType != QgsMeshDatasetGroupMetadata::DataOnVolumes );
288 
289  filter.clearDatasets();
290 
291  std::shared_ptr<QgsMeshMemoryDataset> output = create( filter );
292  output->time = mTimes[0];
293 
294  const QList<int> faceIndexesForRectangle = triangularMesh()->faceIndexesForRectangle( extent );
295  const QVector<int> trianglesToNativeFaces = triangularMesh()->trianglesToNativeFaces();
296 
297  if ( mOutputType == QgsMeshDatasetGroupMetadata::DataOnVertices )
298  {
299  for ( const int faceIndex : faceIndexesForRectangle )
300  {
301  const int nativeIndex = trianglesToNativeFaces[faceIndex];
302  const QgsMeshFace face = nativeMesh()->face( nativeIndex );
303  for ( const int vertexIndex : face )
304  {
305  output->values[vertexIndex].set( D_TRUE );
306  }
307  }
308  }
309  else
310  {
311  for ( const int faceIndex : faceIndexesForRectangle )
312  {
313  const int nativeIndex = trianglesToNativeFaces[faceIndex];
314  output->values[nativeIndex].set( D_TRUE );
315  }
316  }
317  filter.addDataset( output );
318 }
319 
320 
321 void QgsMeshCalcUtils::populateMaskFilter( QgsMeshMemoryDatasetGroup &filter, const QgsGeometry &mask ) const
322 {
323  Q_ASSERT( mOutputType != QgsMeshDatasetGroupMetadata::DataOnVolumes );
324 
325  filter.clearDatasets();
326  std::shared_ptr<QgsMeshMemoryDataset> output = create( filter );
327  output->time = mTimes[0];
328 
329  const QVector<QgsMeshVertex> &vertices = triangularMesh()->vertices();
330 
331  if ( mOutputType == QgsMeshDatasetGroupMetadata::DataOnVertices )
332  {
333  int nativeVertexCount = mMeshLayer->dataProvider()->vertexCount();
334 
335  for ( int i = 0; i < nativeVertexCount; ++i )
336  {
337  const QgsPointXY point( vertices[i] );
338  if ( mask.contains( &point ) )
339  {
340  output->values[i].set( D_TRUE );
341  }
342  else
343  {
344  output->values[i].set( D_FALSE );
345  }
346  }
347  }
348  else
349  {
350  const QVector<QgsMeshFace> &triangles = triangularMesh()->triangles();
351  for ( int i = 0; i < triangles.size(); ++i )
352  {
353  const QgsMeshFace face = triangles[i];
354  const QgsGeometry geom = QgsMeshUtils::toGeometry( face, vertices );
355  const QgsRectangle bbox = geom.boundingBox();
356  if ( mask.intersects( bbox ) )
357  {
358  output->values[i].set( D_TRUE );
359  }
360  else
361  {
362  output->values[i].set( D_FALSE );
363  }
364  }
365  }
366  filter.addDataset( output );
367 }
368 
369 std::shared_ptr<QgsMeshMemoryDataset> QgsMeshCalcUtils::number( double val, double time ) const
370 {
371  Q_ASSERT( isValid() );
372 
373  std::shared_ptr<QgsMeshMemoryDataset> output = create( mOutputType );
374  output->time = time;
375 
376  // by default it is initialized to 1
377  if ( std::isnan( val ) )
378  {
379  if ( mOutputType == QgsMeshDatasetGroupMetadata::DataOnVertices )
380  memset( output->active.data(), 0, static_cast<size_t>( output->active.size() ) * sizeof( int ) );
381  }
382  else
383  {
384  for ( int i = 0; i < output->values.size(); ++i ) // Using for loop we are initializing
385  {
386  output->values[i].set( val );
387  }
388  }
389 
390  return output;
391 }
392 
393 void QgsMeshCalcUtils::number( QgsMeshMemoryDatasetGroup &group1, double val ) const
394 {
395  Q_ASSERT( isValid() );
396 
397  group1.datasets.clear();
398  std::shared_ptr<QgsMeshMemoryDataset> output = number( val, mTimes[0] );
399  group1.datasets.push_back( output );
400 }
401 
402 
403 void QgsMeshCalcUtils::ones( QgsMeshMemoryDatasetGroup &group1 ) const
404 {
405  Q_ASSERT( isValid() );
406  number( group1, 1.0 );
407 }
408 
409 void QgsMeshCalcUtils::nodata( QgsMeshMemoryDatasetGroup &group1 ) const
410 {
411  Q_ASSERT( isValid() );
412  number( group1, D_NODATA );
413 }
414 
415 
416 std::shared_ptr<QgsMeshMemoryDataset> QgsMeshCalcUtils::copy(
417  std::shared_ptr<const QgsMeshMemoryDataset> dataset0
418 ) const
419 {
420  Q_ASSERT( isValid() );
421  Q_ASSERT( dataset0 );
422 
423  std::shared_ptr<QgsMeshMemoryDataset> output = std::make_shared<QgsMeshMemoryDataset>();
424  output->values = dataset0->values; //deep copy
425  output->active = dataset0->active; //deep copy
426  output->time = dataset0->time;
427  output->valid = dataset0->valid;
428  return output;
429 }
430 
431 void QgsMeshCalcUtils::copy( QgsMeshMemoryDatasetGroup &group1, const QString &groupName ) const
432 {
433  Q_ASSERT( isValid() );
434 
435  std::shared_ptr<const QgsMeshMemoryDatasetGroup> group2 = group( groupName );
436  Q_ASSERT( group2 );
437 
438  if ( group2->datasetCount() == 1 )
439  {
440  // Always copy
441  std::shared_ptr<const QgsMeshMemoryDataset> o0 = group2->constDataset( 0 );
442  std::shared_ptr<QgsMeshMemoryDataset> output = copy( o0 );
443  group1.addDataset( output );
444  }
445  else
446  {
447  for ( int output_index = 0; output_index < group2->datasetCount(); ++output_index )
448  {
449  std::shared_ptr<const QgsMeshMemoryDataset> o0 = group2->constDataset( output_index );
450  if ( qgsDoubleNear( o0->time, mTimes.first() ) ||
451  qgsDoubleNear( o0->time, mTimes.last() ) ||
452  ( ( o0->time >= mTimes.first() ) && ( o0->time <= mTimes.last() ) )
453  )
454  {
455  std::shared_ptr<QgsMeshMemoryDataset> output = copy( o0 );
456  group1.addDataset( output );
457  }
458  }
459  }
460 }
461 
462 void QgsMeshCalcUtils::transferDatasets( QgsMeshMemoryDatasetGroup &group1, QgsMeshMemoryDatasetGroup &group2 ) const
463 {
464  Q_ASSERT( isValid() );
465 
466  group1.clearDatasets();
467  for ( int i = 0; i < group2.datasetCount(); ++i )
468  {
469  std::shared_ptr<QgsMeshMemoryDataset> o = group2.datasets[i];
470  Q_ASSERT( o );
471  group1.addDataset( o );
472  }
473  group2.clearDatasets();
474 }
475 
476 void QgsMeshCalcUtils::expand( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
477 {
478  Q_ASSERT( isValid() );
479 
480  if ( group2.datasetCount() > 1 )
481  {
482  if ( group1.datasetCount() == 1 )
483  {
484  const std::shared_ptr<QgsMeshMemoryDataset> o0 = group1.datasets[0];
485  Q_ASSERT( o0 );
486  for ( int i = 1; i < group2.datasetCount(); ++i )
487  {
488  std::shared_ptr<QgsMeshMemoryDataset> o = copy( o0 );
489  o->time = mTimes[i];
490  group1.addDataset( o );
491  }
492  }
493  }
494 }
495 
496 
497 std::shared_ptr<QgsMeshMemoryDataset> QgsMeshCalcUtils::canditateDataset(
498  QgsMeshMemoryDatasetGroup &group,
499  int datasetIndex ) const
500 {
501  Q_ASSERT( isValid() );
502 
503  if ( group.datasetCount() > 1 )
504  {
505  Q_ASSERT( group.datasetCount() > datasetIndex );
506  return group.datasets[datasetIndex];
507  }
508  else
509  {
510  Q_ASSERT( group.datasetCount() == 1 );
511  return group.datasets[0];
512  }
513 }
514 
515 std::shared_ptr<const QgsMeshMemoryDataset> QgsMeshCalcUtils::constCandidateDataset(
516  const QgsMeshMemoryDatasetGroup &group,
517  int datasetIndex ) const
518 {
519  Q_ASSERT( isValid() );
520 
521  if ( group.datasetCount() > 1 )
522  {
523  Q_ASSERT( group.datasetCount() > datasetIndex );
524  return group.constDataset( datasetIndex );
525  }
526  else
527  {
528  Q_ASSERT( group.datasetCount() == 1 );
529  return group.constDataset( 0 );
530  }
531 }
532 
533 int QgsMeshCalcUtils::datasetCount(
534  const QgsMeshMemoryDatasetGroup &group1,
535  const QgsMeshMemoryDatasetGroup &group2 ) const
536 {
537  Q_ASSERT( isValid() );
538 
539  if ( ( group1.datasetCount() > 1 ) || ( group2.datasetCount() > 1 ) )
540  {
541  return mTimes.size();
542  }
543  else
544  {
545  return 1;
546  }
547 }
548 
549 void QgsMeshCalcUtils::func1( QgsMeshMemoryDatasetGroup &group,
550  std::function<double( double )> func ) const
551 {
552  Q_ASSERT( isValid() );
553 
554  for ( int time_index = 0; time_index < group.datasetCount(); ++time_index )
555  {
556  std::shared_ptr<QgsMeshMemoryDataset> output = canditateDataset( group, time_index );
557 
558  for ( int n = 0; n < output->values.size(); ++n )
559  {
560  double val1 = output->values[n].scalar();
561  double res_val = D_NODATA;
562  if ( !std::isnan( val1 ) )
563  res_val = func( val1 );
564  output->values[n] = res_val;
565  }
566 
568  activate( output );
569  }
570 }
571 
572 
573 void QgsMeshCalcUtils::func2( QgsMeshMemoryDatasetGroup &group1,
574  const QgsMeshMemoryDatasetGroup &group2,
575  std::function<double( double, double )> func ) const
576 {
577  Q_ASSERT( isValid() );
578  Q_ASSERT( group1.type == group2.type ); // we do not support mixed output types
579 
580  expand( group1, group2 );
581 
582  for ( int time_index = 0; time_index < datasetCount( group1, group2 ); ++time_index )
583  {
584  std::shared_ptr<QgsMeshMemoryDataset> o1 = canditateDataset( group1, time_index );
585  std::shared_ptr<const QgsMeshMemoryDataset> o2 = constCandidateDataset( group2, time_index );
586 
587  for ( int n = 0; n < o2->values.size(); ++n )
588  {
589  double val1 = o1->values[n].scalar();
590  double val2 = o2->values[n].scalar();
591  double res_val = D_NODATA;
592  if ( !std::isnan( val1 ) && !std::isnan( val2 ) )
593  res_val = func( val1, val2 );
594  o1->values[n] = res_val;
595  }
596 
597  if ( group1.type == QgsMeshDatasetGroupMetadata::DataOnVertices )
598  {
599  activate( o1, o2 );
600  }
601 
602  }
603 }
604 
605 void QgsMeshCalcUtils::funcAggr(
606  QgsMeshMemoryDatasetGroup &group1,
607  std::function<double( QVector<double>& )> func
608 ) const
609 {
610  Q_ASSERT( isValid() );
611 
612  if ( group1.type == QgsMeshDatasetGroupMetadata::DataOnVertices )
613  {
614  std::shared_ptr<QgsMeshMemoryDataset> output = QgsMeshCalcUtils::create( QgsMeshDatasetGroupMetadata::DataOnVertices );
615  output->time = mTimes[0];
616  for ( int n = 0; n < mMeshLayer->dataProvider()->vertexCount(); ++n )
617  {
618  QVector < double > vals;
619  for ( int datasetIndex = 0; datasetIndex < group1.datasetCount(); ++datasetIndex )
620  {
621  const std::shared_ptr<QgsMeshMemoryDataset> o1 = canditateDataset( group1, datasetIndex );
622 
623  double val1 = o1->values[n].scalar();
624  // ideally we should take only values from cells that are active.
625  // but the problem is that the node can be part of multiple cells,
626  // few active and few not, ...
627  if ( !std::isnan( val1 ) )
628  {
629  vals.push_back( val1 );
630  }
631  }
632 
633  double res_val = D_NODATA;
634  if ( !vals.isEmpty() )
635  {
636  res_val = func( vals );
637  }
638 
639  output->values[n] = res_val;
640  }
641 
642  // lets do activation purely on NODATA values as we did aggregation here
643  activate( output );
644 
645  group1.datasets.clear();
646  group1.datasets.push_back( output );
647 
648  }
649  else
650  {
651  std::shared_ptr<QgsMeshMemoryDataset> output = QgsMeshCalcUtils::create( QgsMeshDatasetGroupMetadata::DataOnFaces );
652  output->time = mTimes[0];
653 
654  int facesCount = mMeshLayer->dataProvider()->faceCount();
655  output->values.resize( facesCount );
656 
657  for ( int n = 0; n < mMeshLayer->dataProvider()->faceCount(); ++n )
658  {
659  QVector < double > vals;
660  for ( int datasetIndex = 0; datasetIndex < group1.datasetCount(); ++datasetIndex )
661  {
662  const std::shared_ptr<QgsMeshMemoryDataset> o1 = canditateDataset( group1, datasetIndex );
663  double val1 = o1->values[n].scalar();
664  if ( !std::isnan( val1 ) )
665  {
666  vals.push_back( val1 );
667  }
668  }
669 
670  double res_val = D_NODATA;
671  if ( !vals.isEmpty() )
672  {
673  res_val = func( vals );
674  }
675 
676  output->values[n] = res_val;
677  }
678 
679  group1.datasets.clear();
680  group1.datasets.push_back( output );
681  }
682 }
683 
684 const QgsTriangularMesh *QgsMeshCalcUtils::triangularMesh() const
685 {
686  updateMesh();
687  Q_ASSERT( mMeshLayer->triangularMesh() );
688  return mMeshLayer->triangularMesh();
689 }
690 
691 const QgsMesh *QgsMeshCalcUtils::nativeMesh() const
692 {
693  updateMesh();
694  Q_ASSERT( mMeshLayer->nativeMesh() );
695  return mMeshLayer->nativeMesh();
696 }
697 
698 void QgsMeshCalcUtils::updateMesh() const
699 {
700  if ( ! mMeshLayer->nativeMesh() )
701  {
702  // THIS code is very confusing -- someone please add some explanation as to why a map renderer is created here! (Or better,
703  // add explicit members to do whatever it is that's actually wanted here, instead of creating the map renderer)
704 
705  // we do not care about triangles,
706  // we just want transformed coordinates
707  // of the native mesh. So create
708  // some dummy triangular mesh.
709  QgsMapSettings mapSettings;
710  mapSettings.setExtent( mMeshLayer->extent() );
711  mapSettings.setDestinationCrs( mMeshLayer->crs() );
712  mapSettings.setOutputDpi( 96 );
713  QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
714 
715  delete mMeshLayer->createMapRenderer( context );
716  }
717 }
718 
719 QgsMeshDatasetGroupMetadata::DataType QgsMeshCalcUtils::outputType() const
720 {
721  return mOutputType;
722 }
723 
724 void QgsMeshCalcUtils::addIf( QgsMeshMemoryDatasetGroup &trueGroup,
725  const QgsMeshMemoryDatasetGroup &falseGroup,
726  const QgsMeshMemoryDatasetGroup &condition ) const
727 {
728  Q_ASSERT( isValid() );
729 
730  // Make sure we have enough outputs in the resulting dataset
731  expand( trueGroup, condition );
732  expand( trueGroup, falseGroup );
733 
734  Q_ASSERT( trueGroup.type == falseGroup.type ); // we do not support mixed output types
735  Q_ASSERT( trueGroup.type == condition.type ); // we do not support mixed output types
736 
737  for ( int time_index = 0; time_index < trueGroup.datasetCount(); ++time_index )
738  {
739  std::shared_ptr<QgsMeshMemoryDataset> true_o = canditateDataset( trueGroup, time_index );
740  std::shared_ptr<const QgsMeshMemoryDataset> false_o = constCandidateDataset( falseGroup, time_index );
741  std::shared_ptr<const QgsMeshMemoryDataset> condition_o = constCandidateDataset( condition, time_index );
742  for ( int n = 0; n < true_o->values.size(); ++n )
743  {
744  double conditionValue = condition_o->values[n].scalar();
745  double resultValue = D_NODATA;
746  if ( !std::isnan( conditionValue ) )
747  {
748  if ( qgsDoubleNear( conditionValue, D_TRUE ) )
749  resultValue = true_o->values[n].scalar();
750  else
751  resultValue = false_o->values[n].scalar();
752  }
753  true_o->values[n] = resultValue;
754  }
755 
756  if ( trueGroup.type == QgsMeshDatasetGroupMetadata::DataOnVertices )
757  {
758  // This is not ideal, as we do not check for true/false branch here in activate
759  // problem is that activate is on elements, but condition is on nodes...
760  activate( true_o, condition_o );
761  }
762  }
763 }
764 
765 
766 void QgsMeshCalcUtils::activate( QgsMeshMemoryDatasetGroup &group ) const
767 {
768  Q_ASSERT( isValid() );
769 
770  if ( mOutputType == QgsMeshDatasetGroupMetadata::DataOnVertices )
771  {
772  for ( int datasetIndex = 0; datasetIndex < group.datasetCount(); ++datasetIndex )
773  {
774  std::shared_ptr<QgsMeshMemoryDataset> o1 = canditateDataset( group, datasetIndex );
775  Q_ASSERT( group.type == QgsMeshDatasetGroupMetadata::DataOnVertices );
776  activate( o1 );
777  }
778  }
779  // Groups with data on faces do not have activate flags
780 }
781 
782 void QgsMeshCalcUtils::activate(
783  std::shared_ptr<QgsMeshMemoryDataset> dataset,
784  std::shared_ptr<const QgsMeshMemoryDataset> refDataset /*=0*/
785 ) const
786 {
787 
788  Q_ASSERT( isValid() );
789  Q_ASSERT( dataset );
790 
791  // Activate only faces that has some data and all vertices
792  for ( int idx = 0; idx < mMeshLayer->dataProvider()->faceCount(); ++idx )
793  {
794  if ( refDataset && !refDataset->active.isEmpty() && ( !refDataset->active[idx] ) )
795  {
796  dataset->active[idx] = false;
797  continue;
798  }
799 
800  if ( !dataset->active[idx] )
801  {
802  continue;
803  }
804 
805  QgsMeshFace face = nativeMesh()->face( idx );
806 
807  bool isActive = true; //ACTIVE
808  for ( int j = 0; j < face.size(); ++j )
809  {
810  if ( std::isnan( dataset->values[face[j]].scalar() ) )
811  {
812  isActive = false; //NOT ACTIVE
813  break;
814  }
815  }
816  dataset->active[idx] = isActive;
817  }
818 }
819 
820 double QgsMeshCalcUtils::ffilter( double val1, double filter ) const
821 {
822  Q_ASSERT( !std::isnan( val1 ) );
823 
824  if ( qgsDoubleNear( filter, D_TRUE ) )
825  return val1;
826  else
827  return D_NODATA;
828 }
829 
830 double QgsMeshCalcUtils::fadd( double val1, double val2 ) const
831 {
832  Q_ASSERT( !std::isnan( val1 ) );
833  Q_ASSERT( !std::isnan( val2 ) );
834  return val1 + val2;
835 
836 }
837 
838 double QgsMeshCalcUtils::fsubtract( double val1, double val2 ) const
839 {
840  Q_ASSERT( !std::isnan( val1 ) );
841  Q_ASSERT( !std::isnan( val2 ) );
842  return val1 - val2;
843 
844 }
845 
846 double QgsMeshCalcUtils::fmultiply( double val1, double val2 ) const
847 {
848  Q_ASSERT( !std::isnan( val1 ) );
849  Q_ASSERT( !std::isnan( val2 ) );
850  return val1 * val2;
851 
852 }
853 
854 double QgsMeshCalcUtils::fdivide( double val1, double val2 ) const
855 {
856  Q_ASSERT( !std::isnan( val1 ) );
857  Q_ASSERT( !std::isnan( val2 ) );
858  if ( qgsDoubleNear( val2, 0.0 ) )
859  return D_NODATA;
860  else
861  return val1 / val2;
862 
863 }
864 
865 double QgsMeshCalcUtils::fpower( double val1, double val2 ) const
866 {
867  Q_ASSERT( !std::isnan( val1 ) );
868  Q_ASSERT( !std::isnan( val2 ) );
869  return pow( val1, val2 );
870 
871 }
872 
873 double QgsMeshCalcUtils::fequal( double val1, double val2 ) const
874 {
875  Q_ASSERT( !std::isnan( val1 ) );
876  Q_ASSERT( !std::isnan( val2 ) );
877  if ( qgsDoubleNear( val1, val2 ) )
878  {
879  return D_TRUE;
880  }
881  else
882  {
883  return D_FALSE;
884  }
885 
886 }
887 
888 double QgsMeshCalcUtils::fnotEqual( double val1, double val2 ) const
889 {
890  Q_ASSERT( !std::isnan( val1 ) );
891  Q_ASSERT( !std::isnan( val2 ) );
892  if ( qgsDoubleNear( val1, val2 ) )
893  {
894  return D_FALSE;
895  }
896  else
897  {
898  return D_TRUE;
899  }
900 
901 }
902 
903 double QgsMeshCalcUtils::fgreaterThan( double val1, double val2 ) const
904 {
905  Q_ASSERT( !std::isnan( val1 ) );
906  Q_ASSERT( !std::isnan( val2 ) );
907  if ( val1 > val2 )
908  {
909  return D_TRUE;
910  }
911  else
912  {
913  return D_FALSE;
914  }
915 
916 }
917 
918 double QgsMeshCalcUtils::flesserThan( double val1, double val2 ) const
919 {
920  Q_ASSERT( !std::isnan( val1 ) );
921  Q_ASSERT( !std::isnan( val2 ) );
922  if ( val1 < val2 )
923  {
924  return D_TRUE;
925  }
926  else
927  {
928  return D_FALSE;
929  }
930 
931 }
932 
933 double QgsMeshCalcUtils::flesserEqual( double val1, double val2 ) const
934 {
935  Q_ASSERT( !std::isnan( val1 ) );
936  Q_ASSERT( !std::isnan( val2 ) );
937  if ( val1 <= val2 )
938  {
939  return D_TRUE;
940  }
941  else
942  {
943  return D_FALSE;
944  }
945 
946 }
947 
948 double QgsMeshCalcUtils::fgreaterEqual( double val1, double val2 ) const
949 {
950  Q_ASSERT( !std::isnan( val1 ) );
951  Q_ASSERT( !std::isnan( val2 ) );
952  if ( val1 >= val2 )
953  {
954  return D_TRUE;
955  }
956  else
957  {
958  return D_FALSE;
959  }
960 
961 }
962 
963 
964 double QgsMeshCalcUtils::flogicalAnd( double val1, double val2 ) const
965 {
966  Q_ASSERT( !std::isnan( val1 ) );
967  Q_ASSERT( !std::isnan( val2 ) );
968  bool bval1 = qgsDoubleNear( val1, D_TRUE );
969  bool bval2 = qgsDoubleNear( val2, D_TRUE );
970  if ( bval1 && bval2 )
971  return D_TRUE;
972  else
973  return D_FALSE;
974 
975 }
976 
977 double QgsMeshCalcUtils::flogicalOr( double val1, double val2 ) const
978 {
979  Q_ASSERT( !std::isnan( val1 ) );
980  Q_ASSERT( !std::isnan( val2 ) );
981  bool bval1 = qgsDoubleNear( val1, D_TRUE );
982  bool bval2 = qgsDoubleNear( val2, D_TRUE );
983  if ( bval1 || bval2 )
984  return D_TRUE;
985  else
986  return D_FALSE;
987 
988 }
989 
990 double QgsMeshCalcUtils::flogicalNot( double val1 ) const
991 {
992  Q_ASSERT( !std::isnan( val1 ) );
993  bool bval1 = qgsDoubleNear( val1, D_TRUE );
994  if ( bval1 )
995  return D_FALSE;
996  else
997  return D_TRUE;
998 
999 }
1000 
1001 double QgsMeshCalcUtils::fchangeSign( double val1 ) const
1002 {
1003  Q_ASSERT( !std::isnan( val1 ) );
1004  return -val1;
1005 }
1006 
1007 double QgsMeshCalcUtils::fmin( double val1, double val2 ) const
1008 {
1009  Q_ASSERT( !std::isnan( val1 ) );
1010  if ( val1 > val2 )
1011  {
1012  return val2;
1013  }
1014  else
1015  {
1016  return val1;
1017  }
1018 }
1019 
1020 
1021 double QgsMeshCalcUtils::fmax( double val1, double val2 ) const
1022 {
1023  Q_ASSERT( !std::isnan( val1 ) );
1024  Q_ASSERT( !std::isnan( val2 ) );
1025  if ( val1 < val2 )
1026  {
1027  return val2;
1028  }
1029  else
1030  {
1031  return val1;
1032  }
1033 
1034 }
1035 
1036 double QgsMeshCalcUtils::fabs( double val1 ) const
1037 {
1038  Q_ASSERT( !std::isnan( val1 ) );
1039  if ( val1 > 0 )
1040  {
1041  return val1;
1042  }
1043  else
1044  {
1045  return -val1;
1046  }
1047 
1048 }
1049 
1050 double QgsMeshCalcUtils::fsumAggregated( QVector<double> &vals ) const
1051 {
1052  Q_ASSERT( !vals.contains( D_NODATA ) );
1053  Q_ASSERT( !vals.isEmpty() );
1054  return std::accumulate( vals.begin(), vals.end(), 0.0 );
1055 }
1056 
1057 double QgsMeshCalcUtils::fminimumAggregated( QVector<double> &vals ) const
1058 {
1059  Q_ASSERT( !vals.contains( D_NODATA ) );
1060  Q_ASSERT( !vals.isEmpty() );
1061  return *std::min_element( vals.begin(), vals.end() );
1062 }
1063 
1064 double QgsMeshCalcUtils::fmaximumAggregated( QVector<double> &vals ) const
1065 {
1066  Q_ASSERT( !vals.contains( D_NODATA ) );
1067  Q_ASSERT( !vals.isEmpty() );
1068  return *std::max_element( vals.begin(), vals.end() );
1069 }
1070 
1071 double QgsMeshCalcUtils::faverageAggregated( QVector<double> &vals ) const
1072 {
1073  Q_ASSERT( !vals.contains( D_NODATA ) );
1074  Q_ASSERT( !vals.isEmpty() );
1075  return fsumAggregated( vals ) / vals.size();
1076 }
1077 
1078 void QgsMeshCalcUtils::logicalNot( QgsMeshMemoryDatasetGroup &group1 ) const
1079 {
1080  return func1( group1, std::bind( & QgsMeshCalcUtils::flogicalNot, this, std::placeholders::_1 ) );
1081 }
1082 
1083 void QgsMeshCalcUtils::changeSign( QgsMeshMemoryDatasetGroup &group1 ) const
1084 {
1085  return func1( group1, std::bind( & QgsMeshCalcUtils::fchangeSign, this, std::placeholders::_1 ) );
1086 }
1087 
1088 void QgsMeshCalcUtils::abs( QgsMeshMemoryDatasetGroup &group1 ) const
1089 {
1090  return func1( group1, std::bind( & QgsMeshCalcUtils::fabs, this, std::placeholders::_1 ) );
1091 }
1092 
1093 void QgsMeshCalcUtils::add( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1094 {
1095  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fadd, this, std::placeholders::_1, std::placeholders::_2 ) );
1096 }
1097 
1098 void QgsMeshCalcUtils::subtract( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1099 {
1100  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fsubtract, this, std::placeholders::_1, std::placeholders::_2 ) );
1101 }
1102 
1103 void QgsMeshCalcUtils::multiply( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1104 {
1105  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fmultiply, this, std::placeholders::_1, std::placeholders::_2 ) );
1106 }
1107 
1108 void QgsMeshCalcUtils::divide( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1109 {
1110  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fdivide, this, std::placeholders::_1, std::placeholders::_2 ) );
1111 }
1112 
1113 void QgsMeshCalcUtils::power( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1114 {
1115  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fpower, this, std::placeholders::_1, std::placeholders::_2 ) );
1116 }
1117 
1118 void QgsMeshCalcUtils::equal( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1119 {
1120  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fequal, this, std::placeholders::_1, std::placeholders::_2 ) );
1121 }
1122 
1123 void QgsMeshCalcUtils::notEqual( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1124 {
1125  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fnotEqual, this, std::placeholders::_1, std::placeholders::_2 ) );
1126 }
1127 
1128 void QgsMeshCalcUtils::greaterThan( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1129 {
1130  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fgreaterThan, this, std::placeholders::_1, std::placeholders::_2 ) );
1131 }
1132 
1133 void QgsMeshCalcUtils::lesserThan( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1134 {
1135  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::flesserThan, this, std::placeholders::_1, std::placeholders::_2 ) );
1136 }
1137 
1138 void QgsMeshCalcUtils::lesserEqual( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1139 {
1140  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::flesserEqual, this, std::placeholders::_1, std::placeholders::_2 ) );
1141 }
1142 
1143 void QgsMeshCalcUtils::greaterEqual( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1144 {
1145  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fgreaterEqual, this, std::placeholders::_1, std::placeholders::_2 ) );
1146 }
1147 
1148 void QgsMeshCalcUtils::logicalAnd( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1149 {
1150  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::flogicalAnd, this, std::placeholders::_1, std::placeholders::_2 ) );
1151 }
1152 
1153 void QgsMeshCalcUtils::logicalOr( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1154 {
1155  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::flogicalOr, this, std::placeholders::_1, std::placeholders::_2 ) );
1156 }
1157 
1158 void QgsMeshCalcUtils::minimum( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1159 {
1160  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fmin, this, std::placeholders::_1, std::placeholders::_2 ) );
1161 }
1162 
1163 void QgsMeshCalcUtils::maximum( QgsMeshMemoryDatasetGroup &group1, const QgsMeshMemoryDatasetGroup &group2 ) const
1164 {
1165  return func2( group1, group2, std::bind( & QgsMeshCalcUtils::fmax, this, std::placeholders::_1, std::placeholders::_2 ) );
1166 }
1167 
1168 QgsMeshDatasetGroupMetadata::DataType QgsMeshCalcUtils::determineResultDataType( QgsMeshLayer *layer, const QStringList &usedGroupNames )
1169 {
1170  QHash<QString, int> names;
1171  const QgsMeshDataProvider *dp = layer->dataProvider();
1172  for ( int groupId = 0; groupId < dp->datasetGroupCount(); ++groupId )
1173  {
1174  const auto meta = dp->datasetGroupMetadata( groupId );
1175  const QString name = meta.name();
1176  names[ name ] = groupId;
1177  }
1178  for ( const auto &datasetGroupName : usedGroupNames )
1179  {
1180  if ( names.contains( datasetGroupName ) )
1181  {
1182  int groupId = names.value( datasetGroupName );
1183  const auto meta = dp->datasetGroupMetadata( groupId );
1184  if ( meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices )
1185  {
1187  }
1188  }
1189  }
1191 }
1192 
1193 void QgsMeshCalcUtils::filter( QgsMeshMemoryDatasetGroup &group1, const QgsRectangle &extent ) const
1194 {
1195  QgsMeshMemoryDatasetGroup filter( "filter", outputType() );
1196  populateSpatialFilter( filter, extent );
1197  return func2( group1, filter, std::bind( & QgsMeshCalcUtils::ffilter, this, std::placeholders::_1, std::placeholders::_2 ) );
1198 }
1199 
1200 void QgsMeshCalcUtils::filter( QgsMeshMemoryDatasetGroup &group1, const QgsGeometry &mask ) const
1201 {
1202  QgsMeshMemoryDatasetGroup filter( "filter", outputType() );
1203  populateMaskFilter( filter, mask );
1204  return func2( group1, filter, std::bind( & QgsMeshCalcUtils::ffilter, this, std::placeholders::_1, std::placeholders::_2 ) );
1205 }
1206 
1207 void QgsMeshCalcUtils::sumAggregated( QgsMeshMemoryDatasetGroup &group1 ) const
1208 {
1209  return funcAggr( group1, std::bind( & QgsMeshCalcUtils::fsumAggregated, this, std::placeholders::_1 ) );
1210 }
1211 
1212 void QgsMeshCalcUtils::minimumAggregated( QgsMeshMemoryDatasetGroup &group1 ) const
1213 {
1214  return funcAggr( group1, std::bind( & QgsMeshCalcUtils::fminimumAggregated, this, std::placeholders::_1 ) );
1215 }
1216 
1217 void QgsMeshCalcUtils::maximumAggregated( QgsMeshMemoryDatasetGroup &group1 ) const
1218 {
1219  return funcAggr( group1, std::bind( & QgsMeshCalcUtils::fmaximumAggregated, this, std::placeholders::_1 ) );
1220 }
1221 
1222 void QgsMeshCalcUtils::averageAggregated( QgsMeshMemoryDatasetGroup &group1 ) const
1223 {
1224  return funcAggr( group1, std::bind( & QgsMeshCalcUtils::faverageAggregated, this, std::placeholders::_1 ) );
1225 }
1226 
CORE_EXPORT QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
A rectangle specified with double values.
Definition: qgsrectangle.h:41
void setExtent(const QgsRectangle &rect, bool magnified=true)
Set coordinates of the rectangle which should be rendered.
Triangular/Derived Mesh is mesh with vertices in map coordinates.
QString name() const
Returns name of the dataset group.
virtual int faceCount() const =0
Returns number of faces in the native mesh.
A class to represent a 2D point.
Definition: qgspointxy.h:43
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
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...
virtual int vertexCount() const =0
Returns number of vertices in the native mesh.
void setOutputDpi(double dpi)
Sets DPI used for conversion between real world units (e.g. mm) and pixels.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering)
For each vertex does a simple average of values defined for all faces that contains given vertex...
The QgsMapSettings class contains configuration for rendering of the map.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
bool active(int index) const
Returns a value for active flag by the index For scalar and vector 2d the behavior is undefined...
QgsRectangle extent() const override
Returns the extent of the layer.
QVector< double > values() const
Returns buffer to the array with values For vector it is pairs (x1, y1, x2, y2, ...
DataType
Location of where data is specified for datasets in the dataset group.
QgsMeshDataProvider * dataProvider() override
Returns the layer&#39;s data provider, it may be nullptr.
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) override
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context...
Base class for providing data for QgsMeshLayer.
Contains information about the context of a rendering operation.
Mesh - vertices and faces.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
bool isValid() const
Whether the block is valid.
QVector< int > QgsMeshFace
List of vertex indexes.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
QgsMeshDatasetValue value(int index) const
Returns a value represented by the index For active flag the behavior is undefined.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:91
virtual int datasetGroupCount() const =0
Returns number of datasets groups loaded.
QgsMeshDatasetValue represents single dataset value.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsTriangularMesh * triangularMesh()
Returns triangular mesh (nullptr before rendering)
void set(const QgsPointXY &p1, const QgsPointXY &p2)
Sets the rectangle from two QgsPoints.
Definition: qgsrectangle.h:105
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.