1 /***************************************************************************
2                          qgsmesh3daveraging.cpp
3                          ----------------------
4     begin                : November 2019
5     copyright            : (C) 2019 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  ***************************************************************************/
17 
18 #include <memory>
19 
20 #include "qgsmesh3daveraging.h"
21 #include "qgsmeshdataprovider.h"
22 #include "qgsmeshrenderersettings.h"
23 #include "qgsfeedback.h"
24 
25 // threshold for length intervals, to avoid division by 0
26 static const double eps = 1e-6;
27 
QgsMesh3dAveragingMethod(Method method)28 QgsMesh3dAveragingMethod::QgsMesh3dAveragingMethod( Method method )
29   : mMethod( method )
30 {
31 }
32 
createFromXml(const QDomElement & elem)33 QgsMesh3dAveragingMethod *QgsMesh3dAveragingMethod::createFromXml( const QDomElement &elem )
34 {
35   std::unique_ptr<QgsMesh3dAveragingMethod> ret;
36 
37   const QgsMesh3dAveragingMethod::Method method = static_cast<QgsMesh3dAveragingMethod::Method>(
38         elem.attribute( QStringLiteral( "method" ) ).toInt() );
39   switch ( method )
40   {
41     case QgsMesh3dAveragingMethod::MultiLevelsAveragingMethod:
42       ret.reset( new QgsMeshMultiLevelsAveragingMethod() );
43       break;
44     case QgsMesh3dAveragingMethod::SigmaAveragingMethod:
45       ret.reset( new QgsMeshSigmaAveragingMethod() );
46       break;
47     case QgsMesh3dAveragingMethod::RelativeHeightAveragingMethod:
48       ret.reset( new QgsMeshRelativeHeightAveragingMethod() );
49       break;
50     case QgsMesh3dAveragingMethod::ElevationAveragingMethod:
51       ret.reset( new QgsMeshElevationAveragingMethod() );
52       break;
53   }
54   ret->readXml( elem );
55   return ret.release();
56 }
57 
calculate(const QgsMesh3dDataBlock & block3d,QgsFeedback * feedback) const58 QgsMeshDataBlock QgsMesh3dAveragingMethod::calculate( const QgsMesh3dDataBlock &block3d, QgsFeedback *feedback ) const
59 {
60   if ( !block3d.isValid() )
61     return QgsMeshDataBlock();
62 
63   if ( !hasValidInputs() )
64     return QgsMeshDataBlock();
65 
66   const bool isVector = block3d.isVector();
67   const int count = block3d.count();
68   QgsMeshDataBlock result( isVector ? QgsMeshDataBlock::Vector2DDouble : QgsMeshDataBlock::ScalarDouble, count );
69   QVector<double> valuesFaces( isVector ? 2 * count : count, std::numeric_limits<double>::quiet_NaN() );
70   const QVector<int> verticalLevelsCount = block3d.verticalLevelsCount();
71   const QVector<double> verticalLevels = block3d.verticalLevels();
72   const QVector<double> volumeValues = block3d.values();
73 
74   int startVolumeIndex = 0;
75   for ( int faceIndex = 0; faceIndex < count; ++faceIndex )
76   {
77     if ( feedback && feedback->isCanceled() )
78     {
79       return QgsMeshDataBlock();
80     }
81 
82     const int volumesBelowFaceCount = verticalLevelsCount[faceIndex];
83     if ( volumesBelowFaceCount <= 0 )
84       continue;
85 
86     const int startVerticalLevelIndex = startVolumeIndex + faceIndex;
87     Q_ASSERT( verticalLevels.size() >= startVerticalLevelIndex + volumesBelowFaceCount + 1 );
88     QVector<double> verticalLevelsForFace = verticalLevels.mid( startVerticalLevelIndex, volumesBelowFaceCount + 1 );
89     double faceLevelTop = verticalLevelsForFace[0];
90     double faceLevelBottom = verticalLevelsForFace[verticalLevelsForFace.size() - 1];
91 
92     // the level is value below surface, so top level (-0.1m) is usually higher number than bottom level (e.g. -1.2m)
93     if ( faceLevelTop < faceLevelBottom )
94     {
95       std::swap( faceLevelTop, faceLevelBottom );
96     }
97 
98     double methodLevelTop = std::numeric_limits<double>::quiet_NaN();
99     double methodLevelBottom = std::numeric_limits<double>::quiet_NaN();
100 
101     volumeRangeForFace( methodLevelTop,
102                         methodLevelBottom,
103                         verticalLevelsForFace );
104 
105     if ( !std::isnan( methodLevelTop ) && !std::isnan( methodLevelBottom ) )
106     {
107       // the level is value below surface, so top level (-0.1m) is usually higher number than bottom level (e.g. -1.2m)
108       if ( methodLevelTop < methodLevelBottom )
109       {
110         std::swap( methodLevelTop, methodLevelBottom );
111       }
112 
113       // check if we are completely outside the limits
114       if ( ( methodLevelTop >= faceLevelBottom ) && ( methodLevelBottom <= faceLevelTop ) )
115       {
116         averageVolumeValuesForFace(
117           faceIndex,
118           volumesBelowFaceCount,
119           startVolumeIndex,
120           methodLevelTop,
121           methodLevelBottom,
122           isVector,
123           verticalLevelsForFace,
124           volumeValues,
125           valuesFaces
126         );
127       }
128     }
129 
130     // move to next face and associated volumes
131     startVolumeIndex += volumesBelowFaceCount;
132   }
133   result.setValues( valuesFaces );
134   return result;
135 }
136 
method() const137 QgsMesh3dAveragingMethod::Method QgsMesh3dAveragingMethod::method() const
138 {
139   return mMethod;
140 }
141 
averageVolumeValuesForFace(int faceIndex,int volumesBelowFaceCount,int startVolumeIndex,double methodLevelTop,double methodLevelBottom,bool isVector,const QVector<double> & verticalLevelsForFace,const QVector<double> & volumeValues,QVector<double> & valuesFaces) const142 void QgsMesh3dAveragingMethod::averageVolumeValuesForFace(
143   int faceIndex,
144   int volumesBelowFaceCount,
145   int startVolumeIndex,
146   double methodLevelTop,
147   double methodLevelBottom,
148   bool isVector,
149   const QVector<double> &verticalLevelsForFace,
150   const QVector<double> &volumeValues,
151   QVector<double> &valuesFaces
152 ) const
153 {
154   double totalAveragedHeight = 0;
155   double nSumX = 0.0;
156   double nSumY = 0.0;
157 
158   // Now go through all volumes below face and check if we need to take that volume into consideration
159   for ( int relativeVolumeIndex = 0; relativeVolumeIndex < volumesBelowFaceCount; ++relativeVolumeIndex )
160   {
161     const int volumeIndex = startVolumeIndex + relativeVolumeIndex;
162     double volumeLevelTop = verticalLevelsForFace[relativeVolumeIndex];
163     double volumeLevelBottom = verticalLevelsForFace[relativeVolumeIndex + 1];
164     if ( volumeLevelTop < volumeLevelBottom )
165     {
166       std::swap( volumeLevelTop, volumeLevelBottom );
167     }
168 
169     const double intersectionLevelTop = std::min( methodLevelTop, volumeLevelTop );
170     const double intersectionLevelBottom = std::max( methodLevelBottom, volumeLevelBottom );
171     const double effectiveInterval = intersectionLevelTop - intersectionLevelBottom;
172 
173     if ( effectiveInterval > eps )
174     {
175       if ( isVector )
176       {
177         const double x = volumeValues[2 * volumeIndex ];
178         const double y = volumeValues[ 2 * volumeIndex + 1 ];
179         if ( ! std::isnan( x ) &&
180              ! std::isnan( y )
181            )
182         {
183           nSumX += x * effectiveInterval;
184           nSumY += y * effectiveInterval;
185           totalAveragedHeight += effectiveInterval;
186         }
187       }
188       else
189       {
190         const double x = volumeValues[ volumeIndex ];
191         if ( ! std::isnan( x ) )
192         {
193           nSumX += x * effectiveInterval;
194           totalAveragedHeight += effectiveInterval;
195         }
196       }
197     }
198   }
199 
200   // calculate average
201   if ( totalAveragedHeight > eps )
202   {
203     if ( isVector )
204     {
205       valuesFaces[2 * faceIndex] = nSumX / totalAveragedHeight;
206       valuesFaces[2 * faceIndex + 1 ] = nSumY / totalAveragedHeight;
207     }
208     else
209     {
210       valuesFaces[faceIndex] = nSumX / totalAveragedHeight;
211     }
212   }
213 }
214 
equals(const QgsMesh3dAveragingMethod * a,const QgsMesh3dAveragingMethod * b)215 bool QgsMesh3dAveragingMethod::equals( const QgsMesh3dAveragingMethod *a, const QgsMesh3dAveragingMethod *b )
216 {
217   if ( a )
218     return a->equals( b );
219   else
220     return !b;
221 }
222 
QgsMeshMultiLevelsAveragingMethod(int startLevel,int endLevel,bool countedFromTop)223 QgsMeshMultiLevelsAveragingMethod::QgsMeshMultiLevelsAveragingMethod( int startLevel, int endLevel, bool countedFromTop )
224   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::MultiLevelsAveragingMethod )
225   , mStartVerticalLevel( startLevel )
226   , mEndVerticalLevel( endLevel )
227   , mCountedFromTop( countedFromTop )
228 {
229   if ( mStartVerticalLevel > mEndVerticalLevel )
230   {
231     std::swap( mStartVerticalLevel, mEndVerticalLevel );
232   }
233 }
234 
QgsMeshMultiLevelsAveragingMethod()235 QgsMeshMultiLevelsAveragingMethod::QgsMeshMultiLevelsAveragingMethod()
236   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::MultiLevelsAveragingMethod )
237 {
238 }
239 
QgsMeshMultiLevelsAveragingMethod(int verticalLevel,bool countedFromTop)240 QgsMeshMultiLevelsAveragingMethod::QgsMeshMultiLevelsAveragingMethod( int verticalLevel, bool countedFromTop )
241   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::MultiLevelsAveragingMethod )
242   , mStartVerticalLevel( verticalLevel )
243   , mEndVerticalLevel( verticalLevel )
244   , mCountedFromTop( countedFromTop )
245 {
246 }
247 
248 QgsMeshMultiLevelsAveragingMethod::~QgsMeshMultiLevelsAveragingMethod() = default;
249 
writeXml(QDomDocument & doc) const250 QDomElement QgsMeshMultiLevelsAveragingMethod::writeXml( QDomDocument &doc ) const
251 {
252   QDomElement elem = doc.createElement( QStringLiteral( "multi-vertical-layers-settings" ) );
253   elem.setAttribute( QStringLiteral( "start-layer-index" ), startVerticalLevel() );
254   elem.setAttribute( QStringLiteral( "end-layer-index" ), endVerticalLevel() );
255   return elem;
256 }
257 
readXml(const QDomElement & elem)258 void QgsMeshMultiLevelsAveragingMethod::readXml( const QDomElement &elem )
259 {
260   const QDomElement settings = elem.firstChildElement( QStringLiteral( "multi-vertical-layers-settings" ) );
261   if ( !settings.isNull() )
262   {
263     mStartVerticalLevel = settings.attribute( QStringLiteral( "start-layer-index" ) ).toInt();
264     mEndVerticalLevel = settings.attribute( QStringLiteral( "end-layer-index" ) ).toInt();
265     if ( mStartVerticalLevel > mEndVerticalLevel )
266     {
267       std::swap( mStartVerticalLevel, mEndVerticalLevel );
268     }
269   }
270 }
271 
equals(const QgsMesh3dAveragingMethod * other) const272 bool QgsMeshMultiLevelsAveragingMethod::equals( const QgsMesh3dAveragingMethod *other ) const
273 {
274   if ( !other || other->method() != method() )
275     return false;
276 
277   const QgsMeshMultiLevelsAveragingMethod *otherMethod = static_cast<const QgsMeshMultiLevelsAveragingMethod *>( other );
278 
279   return ( otherMethod->startVerticalLevel() == startVerticalLevel() ) &&
280          ( otherMethod->endVerticalLevel() == endVerticalLevel() ) &&
281          ( otherMethod->countedFromTop() == countedFromTop() );
282 }
283 
clone() const284 QgsMesh3dAveragingMethod *QgsMeshMultiLevelsAveragingMethod::clone() const
285 {
286   return new QgsMeshMultiLevelsAveragingMethod( startVerticalLevel(), endVerticalLevel(), countedFromTop() );
287 }
288 
289 
startVerticalLevel() const290 int QgsMeshMultiLevelsAveragingMethod::startVerticalLevel() const
291 {
292   return mStartVerticalLevel;
293 }
294 
endVerticalLevel() const295 int QgsMeshMultiLevelsAveragingMethod::endVerticalLevel() const
296 {
297   return mEndVerticalLevel;
298 }
299 
hasValidInputs() const300 bool QgsMeshMultiLevelsAveragingMethod::hasValidInputs() const
301 {
302   return mStartVerticalLevel >= 1 && mEndVerticalLevel >= mStartVerticalLevel;
303 }
304 
volumeRangeForFace(double & startVerticalLevel,double & endVerticalLevel,const QVector<double> & verticalLevels) const305 void QgsMeshMultiLevelsAveragingMethod::volumeRangeForFace(
306   double &startVerticalLevel,
307   double &endVerticalLevel,
308   const QVector<double> &verticalLevels
309 ) const
310 {
311   Q_ASSERT( mStartVerticalLevel <= mEndVerticalLevel );
312 
313   if ( countedFromTop() )
314   {
315     const int startIndex = mStartVerticalLevel - 1;
316     if ( startIndex >= 0 && startIndex < verticalLevels.size() )
317     {
318       startVerticalLevel = verticalLevels[ startIndex ];
319     }
320 
321     if ( mEndVerticalLevel >= 0 && mEndVerticalLevel < verticalLevels.size() )
322     {
323       endVerticalLevel = verticalLevels[ mEndVerticalLevel ];
324     }
325     else
326     {
327       endVerticalLevel = verticalLevels[ verticalLevels.size() - 1 ];
328     }
329   }
330   else
331   {
332     const int volumesBelowFaceCount = verticalLevels.size() - 1;
333     const int startIndex = volumesBelowFaceCount - mEndVerticalLevel;
334     if ( startIndex >= 0 && startIndex < verticalLevels.size() )
335     {
336       startVerticalLevel = verticalLevels[ startIndex ];
337     }
338     else
339     {
340       startVerticalLevel = verticalLevels[ 0 ];
341     }
342 
343     const int endIndex = volumesBelowFaceCount - mStartVerticalLevel + 1;
344     if ( endIndex >= 0 && endIndex < verticalLevels.size() )
345     {
346       endVerticalLevel = verticalLevels[ endIndex ];
347     }
348   }
349 }
350 
QgsMeshSigmaAveragingMethod()351 QgsMeshSigmaAveragingMethod::QgsMeshSigmaAveragingMethod()
352   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::SigmaAveragingMethod )
353 {
354 }
355 
QgsMeshSigmaAveragingMethod(double startFraction,double endFraction)356 QgsMeshSigmaAveragingMethod::QgsMeshSigmaAveragingMethod( double startFraction, double endFraction )
357   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::SigmaAveragingMethod )
358   , mStartFraction( startFraction )
359   , mEndFraction( endFraction )
360 {
361   if ( mStartFraction > mEndFraction )
362   {
363     std::swap( mStartFraction, mEndFraction );
364   }
365 }
366 
367 QgsMeshSigmaAveragingMethod::~QgsMeshSigmaAveragingMethod() = default;
368 
writeXml(QDomDocument & doc) const369 QDomElement QgsMeshSigmaAveragingMethod::writeXml( QDomDocument &doc ) const
370 {
371   QDomElement elem = doc.createElement( QStringLiteral( "sigma-settings" ) );
372   elem.setAttribute( QStringLiteral( "start-fraction" ), startFraction() );
373   elem.setAttribute( QStringLiteral( "end-fraction" ), endFraction() );
374   return elem;
375 }
376 
readXml(const QDomElement & elem)377 void QgsMeshSigmaAveragingMethod::readXml( const QDomElement &elem )
378 {
379   const QDomElement settings = elem.firstChildElement( QStringLiteral( "sigma-settings" ) );
380   if ( !settings.isNull() )
381   {
382     mStartFraction = settings.attribute( QStringLiteral( "start-fraction" ) ).toDouble();
383     mEndFraction = settings.attribute( QStringLiteral( "end-fraction" ) ).toDouble();
384     if ( mStartFraction > mEndFraction )
385     {
386       std::swap( mStartFraction, mEndFraction );
387     }
388   }
389 }
390 
equals(const QgsMesh3dAveragingMethod * other) const391 bool QgsMeshSigmaAveragingMethod::equals( const QgsMesh3dAveragingMethod *other ) const
392 {
393   if ( !other || other->method() != method() )
394     return false;
395 
396   const QgsMeshSigmaAveragingMethod *otherMethod = static_cast<const QgsMeshSigmaAveragingMethod *>( other );
397 
398   return qgsDoubleNear( otherMethod->startFraction(), startFraction() ) && qgsDoubleNear( otherMethod->endFraction(), endFraction() ) ;
399 }
400 
clone() const401 QgsMesh3dAveragingMethod *QgsMeshSigmaAveragingMethod::clone() const
402 {
403   return new QgsMeshSigmaAveragingMethod( startFraction(), endFraction() );
404 }
405 
startFraction() const406 double QgsMeshSigmaAveragingMethod::startFraction() const
407 {
408   return mStartFraction;
409 }
410 
endFraction() const411 double QgsMeshSigmaAveragingMethod::endFraction() const
412 {
413   return mEndFraction;
414 }
415 
hasValidInputs() const416 bool QgsMeshSigmaAveragingMethod::hasValidInputs() const
417 {
418   return mStartFraction >= 0 && mEndFraction >= mStartFraction && mEndFraction <= 1;
419 }
420 
volumeRangeForFace(double & startVerticalLevel,double & endVerticalLevel,const QVector<double> & verticalLevels) const421 void QgsMeshSigmaAveragingMethod::volumeRangeForFace(
422   double &startVerticalLevel,
423   double &endVerticalLevel,
424   const QVector<double> &verticalLevels
425 ) const
426 {
427   const double top = verticalLevels[ 0 ];
428   const double bot = verticalLevels[ verticalLevels.size() - 1 ];
429   const double diff = top - bot;
430 
431   if ( mStartFraction < 0 )
432     startVerticalLevel = bot;
433   else
434     startVerticalLevel = bot + diff * mStartFraction;
435 
436   if ( mEndFraction > 1 )
437     endVerticalLevel = top;
438   else
439     endVerticalLevel = bot + diff * mEndFraction;
440 }
441 
countedFromTop() const442 bool QgsMeshMultiLevelsAveragingMethod::countedFromTop() const
443 {
444   return mCountedFromTop;
445 }
446 
isSingleLevel() const447 bool QgsMeshMultiLevelsAveragingMethod::isSingleLevel() const
448 {
449   return mStartVerticalLevel == mEndVerticalLevel;
450 }
451 
452 
QgsMeshRelativeHeightAveragingMethod()453 QgsMeshRelativeHeightAveragingMethod::QgsMeshRelativeHeightAveragingMethod()
454   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::RelativeHeightAveragingMethod )
455 {
456 }
457 
QgsMeshRelativeHeightAveragingMethod(double startDepth,double endDepth,bool countedFromTop)458 QgsMeshRelativeHeightAveragingMethod::QgsMeshRelativeHeightAveragingMethod( double startDepth, double endDepth, bool countedFromTop )
459   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::RelativeHeightAveragingMethod )
460   , mStartHeight( startDepth )
461   , mEndHeight( endDepth )
462   , mCountedFromTop( countedFromTop )
463 {
464   if ( mStartHeight > mEndHeight )
465   {
466     std::swap( mStartHeight, mEndHeight );
467   }
468 }
469 
470 QgsMeshRelativeHeightAveragingMethod::~QgsMeshRelativeHeightAveragingMethod() = default;
471 
writeXml(QDomDocument & doc) const472 QDomElement QgsMeshRelativeHeightAveragingMethod::writeXml( QDomDocument &doc ) const
473 {
474   QDomElement elem = doc.createElement( QStringLiteral( "relative-height-settings" ) );
475   elem.setAttribute( QStringLiteral( "start-height" ), startHeight() );
476   elem.setAttribute( QStringLiteral( "end-height" ), endHeight() );
477   return elem;
478 }
479 
readXml(const QDomElement & elem)480 void QgsMeshRelativeHeightAveragingMethod::readXml( const QDomElement &elem )
481 {
482   const QDomElement settings = elem.firstChildElement( QStringLiteral( "relative-height-settings" ) );
483   if ( !settings.isNull() )
484   {
485     mStartHeight = settings.attribute( QStringLiteral( "start-height" ) ).toDouble();
486     mEndHeight = settings.attribute( QStringLiteral( "end-height" ) ).toDouble();
487     if ( mStartHeight > mEndHeight )
488     {
489       std::swap( mStartHeight, mEndHeight );
490     }
491   }
492 }
493 
equals(const QgsMesh3dAveragingMethod * other) const494 bool QgsMeshRelativeHeightAveragingMethod::equals( const QgsMesh3dAveragingMethod *other ) const
495 {
496   if ( !other || other->method() != method() )
497     return false;
498 
499   const QgsMeshRelativeHeightAveragingMethod *otherMethod = static_cast<const QgsMeshRelativeHeightAveragingMethod *>( other );
500 
501   return qgsDoubleNear( otherMethod->startHeight(), startHeight() ) &&
502          qgsDoubleNear( otherMethod->endHeight(), endHeight() ) &&
503          otherMethod->countedFromTop() == countedFromTop();
504 }
505 
clone() const506 QgsMesh3dAveragingMethod *QgsMeshRelativeHeightAveragingMethod::clone() const
507 {
508   return new QgsMeshRelativeHeightAveragingMethod( startHeight(), endHeight(), countedFromTop() );
509 }
510 
startHeight() const511 double QgsMeshRelativeHeightAveragingMethod::startHeight() const
512 {
513   return mStartHeight;
514 }
515 
endHeight() const516 double QgsMeshRelativeHeightAveragingMethod::endHeight() const
517 {
518   return mEndHeight;
519 }
520 
hasValidInputs() const521 bool QgsMeshRelativeHeightAveragingMethod::hasValidInputs() const
522 {
523   return mStartHeight >= 0 && mEndHeight >= mStartHeight;
524 }
525 
volumeRangeForFace(double & startVerticalLevel,double & endVerticalLevel,const QVector<double> & verticalLevels) const526 void QgsMeshRelativeHeightAveragingMethod::volumeRangeForFace(
527   double &startVerticalLevel,
528   double &endVerticalLevel,
529   const QVector<double> &verticalLevels ) const
530 {
531   if ( countedFromTop() )
532   {
533     const double top = verticalLevels[ 0 ];
534     startVerticalLevel = top - mStartHeight;
535     endVerticalLevel = top - mEndHeight;
536   }
537   else
538   {
539     const double bot = verticalLevels[verticalLevels.size() - 1];
540     startVerticalLevel = bot + mStartHeight;
541     endVerticalLevel = bot + mEndHeight;
542   }
543 }
544 
countedFromTop() const545 bool QgsMeshRelativeHeightAveragingMethod::countedFromTop() const
546 {
547   return mCountedFromTop;
548 }
549 
QgsMeshElevationAveragingMethod()550 QgsMeshElevationAveragingMethod::QgsMeshElevationAveragingMethod()
551   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::ElevationAveragingMethod )
552 {
553 }
554 
QgsMeshElevationAveragingMethod(double startElevation,double endElevation)555 QgsMeshElevationAveragingMethod::QgsMeshElevationAveragingMethod( double startElevation, double endElevation )
556   : QgsMesh3dAveragingMethod( QgsMesh3dAveragingMethod::ElevationAveragingMethod )
557   , mStartElevation( startElevation )
558   , mEndElevation( endElevation )
559 {
560   if ( mEndElevation > mStartElevation )
561   {
562     std::swap( mEndElevation, mStartElevation );
563   }
564 }
565 
566 QgsMeshElevationAveragingMethod::~QgsMeshElevationAveragingMethod() = default;
567 
writeXml(QDomDocument & doc) const568 QDomElement QgsMeshElevationAveragingMethod::writeXml( QDomDocument &doc ) const
569 {
570   QDomElement elem = doc.createElement( QStringLiteral( "elevation-settings" ) );
571   elem.setAttribute( QStringLiteral( "start-elevation" ), startElevation() );
572   elem.setAttribute( QStringLiteral( "end-elevation" ), endElevation() );
573   return elem;
574 }
575 
readXml(const QDomElement & elem)576 void QgsMeshElevationAveragingMethod::readXml( const QDomElement &elem )
577 {
578   const QDomElement settings = elem.firstChildElement( QStringLiteral( "elevation-settings" ) );
579   if ( !settings.isNull() )
580   {
581     mStartElevation = settings.attribute( QStringLiteral( "start-elevation" ) ).toDouble();
582     mEndElevation = settings.attribute( QStringLiteral( "end-elevation" ) ).toDouble();
583     if ( mEndElevation > mStartElevation )
584     {
585       std::swap( mEndElevation, mStartElevation );
586     }
587   }
588 }
589 
equals(const QgsMesh3dAveragingMethod * other) const590 bool QgsMeshElevationAveragingMethod::equals( const QgsMesh3dAveragingMethod *other ) const
591 {
592   if ( !other || other->method() != method() )
593     return false;
594 
595   const QgsMeshElevationAveragingMethod *otherMethod = static_cast<const QgsMeshElevationAveragingMethod *>( other );
596 
597   return qgsDoubleNear( otherMethod->startElevation(), startElevation() ) && qgsDoubleNear( otherMethod->endElevation(), endElevation() ) ;
598 }
599 
clone() const600 QgsMesh3dAveragingMethod *QgsMeshElevationAveragingMethod::clone() const
601 {
602   return new QgsMeshElevationAveragingMethod( startElevation(), endElevation() );
603 }
604 
startElevation() const605 double QgsMeshElevationAveragingMethod::startElevation() const
606 {
607   return mStartElevation;
608 }
609 
endElevation() const610 double QgsMeshElevationAveragingMethod::endElevation() const
611 {
612   return mEndElevation;
613 }
614 
hasValidInputs() const615 bool QgsMeshElevationAveragingMethod::hasValidInputs() const
616 {
617   return mStartElevation <= 0.0 && mEndElevation <= mStartElevation;
618 }
619 
volumeRangeForFace(double & startVerticalLevel,double & endVerticalLevel,const QVector<double> & verticalLevels) const620 void QgsMeshElevationAveragingMethod::volumeRangeForFace(
621   double &startVerticalLevel,
622   double &endVerticalLevel,
623   const QVector<double> &verticalLevels ) const
624 {
625   Q_UNUSED( verticalLevels )
626   startVerticalLevel = mStartElevation;
627   endVerticalLevel = mEndElevation;
628 }
629