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